Getting started with Shiro to integrate redis

1. Understanding of Shiro's overall architecture

User operation enters Security Manager, Security Manager obtains authentication data from Reaml through Authentication authenticator, and obtains authorization data from Reaml through Authorizer

2. Shiro authentication and authorization

Code hint:

SimpleAccountRealm realm = new SimpleAccountRealm();

@Before
public void adduser(){
realm.addAccount("mark", "12345","admin");
}

  

@Test
public void testAuthentication () {
//1. Build the SecurityManager environment
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(realm);
//2. The construction subject submits the authentication request
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//build authentication request
UsernamePasswordToken token = new UsernamePasswordToken("mark", "12345");
//to authenticate
subject.login(token);
/ / Determine whether the authentication is successful, the authentication failure will report an exception
System.out.println("isAuthentication :"+subject.isAuthenticated());
//Check if there is a role
subject.checkRole("admin");
}

  

Here is SimpleAccountReal, Shiro also has two built-in Reamls, IniReaml and JdbcReam
IniReaml uses the format of the configuration file
//Create a new IniReam
IniRealm iniRealm = new IniRealm("classpath:user.ini");

user.ini:

[users]
mark=12345,admin
[roles]
admin=user:delete

 

The format of the database used by JdbcReam
If there is no custom query statement, Shiro will use the default query statement. The default table format is:
The code example is as follows:
Build the data source:

 

DruidDataSource dateSource = new DruidDataSource();
{
dateSource.setUrl("jdbc:mysql://localhost:3306/test");
dateSource.setPassword("root");
dateSource.setUsername("root");
}

  

@Test
public void testAuthentication () {
//Create a new JDBCRealm
JdbcRealm jdbcRealm = new JdbcRealm();
//add data source
jdbcRealm.setDataSource(dateSource);
//Enable the permission function of jdbcReaml
jdbcRealm.setPermissionsLookupEnabled (true);
//1. Build the SecurityManager environment
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
//2. The construction subject submits the authentication request
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//build authentication request
UsernamePasswordToken token = new UsernamePasswordToken("admin", "testadmin");
//to authenticate
subject.login(token);
/ / Determine whether the authentication is successful, the authentication failure will report an exception
System.out.println("isAuthentication :"+subject.isAuthenticated());
//Check if there is a role
subject.checkRole("admin");
// Determine if a certain permission is available
subject.checkPermission("user:delete");
}

 

Custom Realm
1. Custom Realm needs to inherit the AuthorizingRealm class
public class CustomRealm extends AuthorizingRealm{
/**
* Authorization method
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//Get the authenticated information from the subject
String username = (String) principalCollection.getPrimaryPrincipal();
//Get the role of the object
Set <String> roles = getRolesByUsername (username);
//Get the permission of the object
Set<String> permissions = getPermissionsByUsername(username);
//construct return value
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
/**
* Authentication method
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//Get the username from the authentication information transferred by the subject
String username = (String) authenticationToken.getPrincipal();
//Get credentials from database by username
String password = getpasswordbyUserName(username);
if(password==null){
return null;
}
//construct return value
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("cjl",password,"customRealm");
return authenticationInfo;
}
}
Usage is the same as above.
 
Shiro Cryptography
The authentication is used as follows:
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//Encryption method
matcher.setHashIterations(1);//Encryption times
customRealm.setCredentialsMatcher(matcher);//Add encryption method in realm
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("cjl",password,"customRealm");//Add in the custom Realm authentication method
authenticationInfo.setCredentialsSalt (ByteSource.Util.bytes ("cjl"));

Add salt if needed

Shiro Consistency Spring

Step 1. Add shiro's filter in web.xml
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Step 2. Shiro's filter in the spring configuration file

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html = anon
/sublogin = anon
/* = authc
</value>
</property>
</bean>

 

        Parameter explanation:
        anon: No authentication required, direct access
        authc: Access is required after authentication
        Detailed explanation of the shiro filter department later
  Step 3. Create a SecurityManager object
<!-- Create SecurityManager object -->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
</bean>

Step 4. Create a realm object, you can use a custom realm

<bean class="com.shiro.realm.CustomRealm" id="customRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!-- Create encryption method object-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>

Step 5. The login code is as follows:

@RequestMapping(value = "/sublogin",produces = "application/json;charset=utf-8")
@ResponseBody
public String subLogin(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
return e.getMessage();
}
try {
subject.checkPermission("user:delete");
} catch (AuthorizationException e) {
return String.format("No permission %s", e.getMessage());
}
return "Login successful, have permission";
}

Shiro annotation configuration authorization

Step 1. Add pom dependencies

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

Step 2. Add shiro's aop to the spring configuration file

<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
Step 4. Add annotations to the controller that needs to be taken seriously
    @RequiresRoles("admin")
 @RequiresPermissions("user:delete")
Shiro filter
  shiro's built-in filter
  Authentication related: anon (no authentication required), authBasic, authc (requires authentication), user (requires a current user), logout (requires logout)
  Permission-related: perms (permission format ["user:delete", "user"add"]), roles (role-related, same usage), ssl (requires security protocol: https), port (requires port, same format as above)
 
Custom Filter
If it is related to authorization, inherit AuthorizationFilter, and if related to authentication, inherit AuthenticatingFilter
Code example:
public class RolesOrFilter extends AuthorizationFilter{
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest,servletResponse );
String[] roles = ((String[]) o);
if (roles == null && roles.length <= 0) {
for (String role : roles)
if (subject.hasRole(role)) {
return true;
}
return false;
} else {
return true;
}
}
}

 

Configure in spring's configuration file
1. Add the filter
<bean class="com.shiro.controller.filter.RolesOrFilter" id="rolesOrFilter"/>

2. Configure this filter into the shiro filter

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html = anon
/sublogin = anon
/testRolesOr = rolesOr["user:delete","user:add"]
/* = authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr" value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>

  

Finish.
Shiro session management and cache management
For session management, shiro uses AbstractSessionDAO. When customizing, you need to inherit this class and implement the methods in it, such as the method of redis.
public class RedisSessionDao extends AbstractSessionDAO {

@Resource
private JedisUtil jedisUtil;

private final String SHIRO_SESSION_PREFIX= "test-session";

private byte[] getkey(String key){
return (String.format("%s%s", SHIRO_SESSION_PREFIX, key)).getBytes();
}

@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
//Bind sessionId and session
assignSessionId(session,sessionId );
redisSaveSession(session);
return sessionId;
}



private void redisSaveSession(Session session) {
if (session!=null&&session.getId()!=null) {
byte[] key = getkey(session.getId().toString());
byte[] value = SerializationUtils.serialize(session);
jedisUtil.set(key,value);
jedisUtil.expire(key,600);
}
}

@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId==null) {
return null;
}
byte[] key = getkey(sessionId.toString());
byte[] value = jedisUtil.getkey(key);
return (Session) deserialize(value);
}

@Override
public void update(Session session) throws UnknownSessionException {
redisSaveSession(session);
}

@Override
public void delete(Session session) {
if (session==null || session.getId()==null) {
return;
}
jedisUtil.del(getkey(session.getId().toString()));
}

@Override
public Collection<Session> getActiveSessions() {
Set<byte[]> keys = jedisUtil.keys(SHIRO_SESSION_PREFIX);
Set<Session> sessions = new HashSet<>();
if (!isEmpty(keys)) {
for (byte[] key : keys) {
Session session = (Session) SerializationUtils.deserialize(key);
sessions.add(session);
}
return sessions;
} else {
return sessions;
}
}
}

Configure in spring's configuration file

<bean class="com.shiro.session.CustomSessionManager" id="sessionManager">
<property name="sessionDAO" ref="redisSessionDao"/>
</bean>

<bean class="com.shiro.session.RedisSessionDao" id="redisSessionDao"/>

  

<!-- Create SecurityManager object -->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
When using redis, you need to reimplement DefaultWebSessionManager, because each request will send a request to redis to query the session, which will put a lot of pressure on redis. Reimplement the DefaultWebSessionManager class, inherit this class, and overload the retrieveSession method. The first request When the session is placed in the request field.
Cache management
Cache management is similar to session management, the code is as follows
public class RedisCacheManager implements CacheManager{

@Resource
private RedisCache redisCache;


@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
return redisCache;
}
}

  

@Component
public class RedisCache<K,V> implements Cache<K,V> {

@Resource
private JedisUtil jedisUtil;

private final static String CACHE_PREFIX = "test-cache:";

private byte[] getkey(K k){
if(k instanceof String){
return (CACHE_PREFIX + k).getBytes();
}else{
return SerializationUtils.serialize(k);
}
}

@Override
public V get(K k) throws CacheException {
byte[] value = jedisUtil.getkey(getkey(k));
if(value!=null){
System.out.println("Get data from redis");
return (V) SerializationUtils.deserialize(value);
}
return null;
}

@Override
public V put(K k, V v) throws CacheException {
byte[] key = getkey(k);
byte[] value = SerializationUtils.serialize(v);
jedisUtil.set(key,value );
jedisUtil.expire(key,600 );
return v;
}

@Override
public V remove(K k) throws CacheException {
byte[] key = getkey(k);
byte[] value = jedisUtil.getkey(key);
jedisUtil.del(key);
if(value!=null){
return (V) SerializationUtils.deserialize(value);
}
return null;
}

@Override
public void clear() throws CacheException {

}

@Override
public int size() {
return 0;
}

@Override
public Set<K> keys() {
return null;
}

@Override
public Collection<V> values() {
return null;
}
}

Configure in the spring configuration file

<bean class="com.shiro.cache.RedisCacheManager" id="cacheManager"/>

  Finish.

For detailed code see:

https://github.com/caojinlin/shiroTest.git

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325442386&siteId=291194637