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
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"); }
user.ini:
[users] mark=12345,admin [roles] admin=user:delete
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"); }
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; } }
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
<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>
<!-- 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"/>
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; } } }
<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>
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>
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