wiki
1. 核心名词
- subject : 可以是当前操作用户,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物,即当前跟软件交互的东西
- securityManager : 典型的 Facade 模式,它管理内部组件实例,并通过它来提供安全管理的各种服务
- realm : 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”,即当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。Realm 实质上是一个安全相关的 DAO,它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。当配置 Shiro 时,你必须至少指定一个Realm,用于认证和(或)授权。
2. 实战
1. 添加依赖
1 2 3 4 5 6
| <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.8.0</version> </dependency>
|
2. 配置 Shiro
配合 Spring 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
@Configuration public class ShiroConfig {
@Bean public SecurityManager securityManager(Realm myRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm); return securityManager; }
@Bean public MyRealm myRealm(){ MyRealm myRealm = new MyRealm(); return myRealm; }
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setLoginUrl("/login.html"); shiroFilter.setSuccessUrl("/success"); shiroFilter.setUnauthorizedUrl("/noPermission"); Map<String,String> filterChainMap = new LinkedHashMap<String,String>();
filterChainMap.put("/login","anon"); filterChainMap.put("/admin/**","authc"); filterChainMap.put("/user/**","authc"); filterChainMap.put("/**","authc"); shiroFilter.setFilterChainDefinitionMap(filterChainMap); return shiroFilter; } }
|
3. 自定义 Realm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class MyRealm implements Realm { @Override public String getName() { return null; } @Override public boolean supports(AuthenticationToken authenticationToken) { return false; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { return null; } }
|
4. 配置认证账号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
public class MyAuthenticatingRealm extends AuthenticatingRealm {
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String userName = token.getUsername(); String dbUserName = userName; if(!"admin".equals(dbUserName) && !"zhangsan".equals(dbUserName)) { throw new UnknownAccountException("账号错误"); } if("zhangsan".equals(userName)){ throw new LockedAccountException("账号被锁定"); } String dbpassword = "123456";
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("MD5"); credentialsMatcher.setHashIterations(1); this.setCredentialsMatcher(credentialsMatcher);
Object obj = new SimpleHash("MD5",dbpassword,"",1);
return new SimpleAuthenticationInfo(dbUserName,obj.toString(),getName()); } }
|
5. 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); } catch (UnknownAccountException e) { System.out.println("---------------账号不存在"); }catch (LockedAccountException e){ System.out.println("===============账号被锁定"); }catch (IncorrectCredentialsException e){ System.out.println("***************密码不匹配"); } }
subject.logout();
|
6. 基于代码的权限控制
判断用户哪个权限是否可以使用,可以先为用户分配权限
继承 Realm 的实现一个类 AuthorizingRealm,并添加角色信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
public class MyAuthRealm2 extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Object username = principalCollection.getPrimaryPrincipal(); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); Set<String> roles = new HashSet<String>(); if ("admin".equals(username)) { roles.add("admin"); roles.add("user"); } else if ("zhangsan".equals(username)) { roles.add("user"); } Set<String> permission = new HashSet<String>(); if ("admin".equals(username)) { permission.add("admin:add"); } simpleAuthorizationInfo.setRoles(roles); simpleAuthorizationInfo.setStringPermissions(permission); return simpleAuthorizationInfo; } }
|
修改 ShiroConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Configuration public class ShiroConfig {
... @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setLoginUrl("/login.html"); shiroFilter.setSuccessUrl("/success"); shiroFilter.setUnauthorizedUrl("/noPermission"); Map<String,String> filterChainMap = new LinkedHashMap<String,String>(); filterChainMap.put("/login","anon");
filterChainMap.put("/admin/test","authc,perms[admin:add]"); filterChainMap.put("/admin/**","authc,roles[admin]"); filterChainMap.put("/user/**","authc,roles[user]"); shiroFilter.setFilterChainDefinitionMap(filterChainMap); return shiroFilter; } }
|
6. 基于注解的权限控制
修改 ShiroConfig,此时需要删除拦截器的相关配置,如 filterChainMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @Configuration public class ShiroConfig {
...
@Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; }
@Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor=new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @RequiresRoles(value = {"admin"}) @RequestMapping("/admin/test") public @ResponseBody String adminTest(){ return "这个adminTest请求"; }
@RequiresPermissions(value = {"admin:add"}) @RequestMapping("/admin/add") public @ResponseBody String adminAdd(){ Subject subject = SecurityUtils.getSubject(); return "这个adminAdd请求"; }
@ExceptionHandler(value = {Exception.class}) public String myError(Throwable throwable){ System.out.println(throwable.getClass()); System.out.println("---------------------------------"); return "noPermission"; }
|
7. 无 spring 使用
添加依赖后,可以创建 shiro 的配置文件,以 ini 为后缀的文件
1 2 3 4 5 6 7 8 9
| [users] zhangsan=123456,seller lisi=123123,dba admin=admin,admin
[roles] admin=* seller=order-add,order-del,order-list ckmgr=dba-add,dba-del,dba-list
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class ShiroDemo { public static void main(String[] args) { String username = ""; String password = ""; DefaultSecurityManager securityManager = new DefaultSecurityManager(); IniRealm iniRealm = new IniRealm("classpath:shiro.ini"); securityManager.setRealm(iniRealm); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token=new UsernamePasswordToken(username,password); boolean flag = false; try { subject.login(token); flag = true; } catch (IncorrectCredentialsException e) { flag = false; } System.out.println(flag?"登录成功":"登录失败");
System.out.println(subject.hasRole("seller"));
System.out.println(subject.isPermitted("order-del")); } }
|