ApacheShiro集成Spring(二)
1、依赖:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
2、自定义Realm:
package com.example.demo_mg.realm; import org.apache.commons.collections.map.HashedMap; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import java.util.*; public class TestRealm extends AuthorizingRealm { //模拟users、user_roles、roles_permissions三张表的查询,实际应用需要查询数据库或缓存 Map<String, String> users = new HashMap<>(); Map<String, Set<String>> user_roles = new HashedMap(); Map<String, Set<String>> roles_permissions = new HashedMap(); // String salt = UUID.randomUUID().toString().replaceAll("-",""); { //不加盐(与认证对应) users.put("wzs", new Md5Hash("123456",null, 2).toString()); //加盐 // users.put("wzs", new Md5Hash("123456",salt, 2).toString()); user_roles.put("wzs", new HashSet<>(Arrays.asList("admin", "test"))); roles_permissions.put("admin", new HashSet<>(Arrays.asList("user:delete", "user:update"))); super.setName("TestRealm"); //设置Realm名称,可选 } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //从认证信息获取用户名 String username = (String)principalCollection.getPrimaryPrincipal(); //从数据库或缓存中获取角色、权限数据 Set<String> roles = user_roles.get(username); Set<String> permissions = new HashSet<>(); for (String role : roles) { Set<String> set; if((set = roles_permissions.get(role)) != null) { permissions.addAll(set); } } SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setRoles(roles); simpleAuthorizationInfo.setStringPermissions(permissions); return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //从主题传过来的认证信息中,获得用户名 String username = (String)authenticationToken.getPrincipal(); //通过用户名从数据库中获取凭证 String password = users.get(username); if(password != null) { //不加盐 // return new SimpleAuthenticationInfo(username, password, super.getName()); //加盐 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, super.getName()); // simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(salt)); return simpleAuthenticationInfo; } return null; } }
3、配置:
package com.example.demo_mg.config; import com.example.demo_mg.realm.TestRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; @Configuration public class ShiroConfiguration { @Bean public HashedCredentialsMatcher getCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("md5"); credentialsMatcher.setHashIterations(2); return credentialsMatcher; } @Bean public TestRealm getRealm(HashedCredentialsMatcher credentialsMatcher) { TestRealm testRealm = new TestRealm(); testRealm.setCredentialsMatcher(credentialsMatcher); return testRealm; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(TestRealm testRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(testRealm); return securityManager; } @Bean public ShiroFilterFactoryBean getShiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("login.html"); shiroFilterFactoryBean.setUnauthorizedUrl("403.html"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login.html", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/*", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 注解式授权2个bean * @return */ //Shiro生命周期处理器 @Bean public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
4、实体:
package com.example.demo_mg.entity; public class User { private String username; private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
5、控制器:
package com.example.demo_mg.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class LoginContrller { @RequestMapping(value = "/login",method = RequestMethod.GET) public String loginUser(String username, String password) { UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(usernamePasswordToken); //完成登录 //更新用户登录时间,也可以在ShiroRealm里面做 return "index"; } catch(Exception e) { return "login";//返回登录页面 } } @RequestMapping("/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } @RequestMapping("/test") public void test() { System.out.println("test"); } @RequiresRoles("admin") @RequestMapping("/role") public void role() { System.out.println("role"); } @RequiresPermissions("user:delete1") @RequestMapping("/permission") public void permission() { System.out.println("permission"); } }
6、自定义过滤器,授权继承AuthorizationFilter,认证继承AuthenticatinFilter,可以阅读其源码。
Shiro提供的认证过滤器包括anon,authBasic,authc,user,logout,授权过滤器包括perms,roles,ssl,port,举例perms["user:delete","user:update"],roles["admin","user"],方括号内的角色或权限需要同时满足,否则跳到unauthorizadUrl,ssl是https过滤器。
自定义过滤器例子,角色满足一个即可:
package com.example.demo_mg.filter; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class RoleOrFilter 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) { return true; } for (String role : roles) { if(subject.hasRole(role)) { return true; } } return false; } }
拿到subject,o是配置的过滤器方括号里面的数组,return true;表示通过过滤器。
配置类修改使自定义过滤器生效:
@Bean public ShiroFilterFactoryBean getShiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("login.html"); shiroFilterFactoryBean.setUnauthorizedUrl("403.html"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login.html", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/filter", "roleOr[admin,user]"); filterChainDefinitionMap.put("/*", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); //自定义拦截器 LinkedHashMap<String, Filter> filters = new LinkedHashMap<>(); filters.put("roleOr", getRoleOrFilter()); shiroFilterFactoryBean.setFilters(filters); return shiroFilterFactoryBean; } @Bean public RoleOrFilter getRoleOrFilter() { RoleOrFilter roleOrFilter = new RoleOrFilter(); return roleOrFilter; }
测试:
@RequestMapping("/filter") public void filter() { System.out.println("filter"); }