Shiro要点概览与SpringBoot整合实例

1. 简介

概念

说明

Subject

主体,简化点说就是用户实体

Principal

Subject的唯一标识,如id、用户名、手机号、邮箱等

Credential

凭证信息,主体证明自己的东西,如密码、证书等

Authenticator

认证器,对Subject身份进行认证,例如验证用户的用户名和密码是否匹配

Authorizer

授权器,通过认证器认证之后,要访问资源,还得获得资源授权

sessionManager

会话管理,不依赖web容器的session,可以将分布式应用的会话集中在一点管理,实现单点登录,自定义可继承DefaultWebSessionManager

SecurityManager

安全管理器,继承了Authenticator,Authorizer, SessionManager,对全部的subject进行安全管理

Realm

领域,实际认证和授权的地方,因为shiro不知道你的用户和密码相关数据,所以一般需要自定义Realm完成认证和授权

SessionDAO

对session会话操作的一套接口,自定义可继承EnterpriseCacheSessionDAO

CacheManager

缓存管理,将用户权限数据存储在缓存,避免每次访问数据库

Cryptography

密码管理,加密/解密的组件,如:常用的散列、加解密等功能

2. AuthorizingRealm

对于应用来说,Shiro的侵入虽然比较高,但是使用还是相对比较简单,如果不想太麻烦,只需要继承AuthorizingRealm,然后实现:

  1. doGetAutherizationInfo(PrincipalCollection principals)用于授权
  2. doGetAuthenticationInfo(AuthenticationToken token)用于认证

然后配置一下ShiroFilterFactoryBean,告诉Shiro资源的权限就可以。

3. 默认Filter

Shiro通过一系列filter来控制访问权限,并预先定义了很多过滤器(org.apache.shiro.web.filter.mgt.DefaultFilter),常用的过滤器配置如下:

字符串

说明

anon

所有用户可访问

authc

认证用户可访问

user

特定用户能访问

port

特定端口能访问,/user/**=port[8088]

http

特定http能访问,/user/**=perms[user:post]

roles

指定角色用户可访问,/admins/=roles[admin],admins/**=roles["admin,sys"]

perms

指定权限用户可访问,/admins/=perms[user:add:],/admins/**=perms["user:add:,user:modify:*"]

没有登录之前都跳转登录页面,登录之后检查权限,没有权限跳转未授权页面

4. 自定义Filter

自定义Filter可以继承AuthorizationFilter,当然也可以继承AccessControlFilter,或者:

Shiro过滤器

1import org.apache.shiro.subject.Subject; 2import org.apache.shiro.web.filter.AccessControlFilter; 3 4import javax.servlet.ServletRequest; 5import javax.servlet.ServletResponse; 6 7public class OnlineSessionFilter extends AccessControlFilter { 8 9 10 /** 11 * 是否允许访问 12 * @mappedValue [urls]配置中拦截器参数部分 13 * @return 如果:true允许访问,否则不允许访问 14 */ 15 @Override 16 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 17 // 检查逻辑 18 return true; 19 } 20 21 /** 22 * 当访问拒绝时是否继续处理 23 * @return 如果:true表示需要继续处理,否则直接返回 24 */ 25 @Override 26 protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { 27 Subject subject = getSubject(request, response); 28 if (subject != null) { 29 subject.logout(); 30 } 31 saveRequestAndRedirectToLogin(request, response); 32 return false; 33 } 34}

然后在就可以这样使用:

1@Bean 2public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { 3 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 4 Map<String, String> filterChainDefinitionMap = new HashMap<>(); 5 shiroFilterFactoryBean.setSecurityManager(securityManager); 6 shiroFilterFactoryBean.setLoginUrl("/shiro/login"); 7 // 登录成功后要跳转的链接 8 shiroFilterFactoryBean.setSuccessUrl("/shiro/index"); 9 // 访问未授权页面之后跳转链接 10 shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/unauthc"); 11 12 Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); 13 filters.put("onlineSession", new OnlineSessionFilter());//自定义过滤器 14 shiroFilterFactoryBean.setFilters(filters); 15 16 // 所有请求需要认证 17 filterChainDefinitionMap.put("/**", "onlineSession"); 18 19 filterChainDefinitionMap.put("/*", "anon"); 20 filterChainDefinitionMap.put("/shiro/index", "authc"); 21 filterChainDefinitionMap.put("/shiro/admin", "roles[admin]"); 22 filterChainDefinitionMap.put("/shiro/insertUpdate", "perms[create,update]"); 23 filterChainDefinitionMap.put("/shiro/delete", "perms[delete]"); 24 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 25 return shiroFilterFactoryBean; 26}

5. SimpleCredentialsMatcher

因为密码是直接取得数据库的密码,所以想要保证用户传入的密码能和我的密码能够匹配,需要创建自定义的密码校验规则。

可以继承SimpleCredentialsMatcher来实现自己的凭证验证,不过一般都使用HashedCredentialsMatcher,例如Sha256CredentialsMatcher。

6. 注解

注解

说明

@RequiresAuthentication

验证用户是否登录

@RequiresUser

验证用户是否登录,和@RequiresAuthentication不同记住我的用户也算

@RequiresGuest

验证是否是一个游客,没有登录

@RequiresRoles

必须是指定角色@RequiresRoles("admin"),@RequiresRoles(value={"admin", "sys"}, logical=Logical.OR)

@RequiresPermissions

必须有指定权限@RequiresPermissions({"delete", "write"})

7. 实例

如果要简单使用shiro,还是比较容易,下面给一个实例。

7.1 maven依赖

1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-jpa</artifactId> 4</dependency> 5<dependency> 6 <groupId>org.apache.shiro</groupId> 7 <artifactId>shiro-spring</artifactId> 8 <version>1.7.0</version> 9</dependency> 10<dependency> 11 <groupId>org.apache.shiro</groupId> 12 <artifactId>shiro-ehcache</artifactId> 13 <version>1.7.0</version> 14</dependency>

省略了SpringBoot相关的,选择自己喜欢的版本添加,shiro-spring1.7.0的shiro-ehcache现在暂时要自己单独添加一下,我看了它的配置是有,但是在自己项目中引用不了,所以手动添加一下。

7.2 属性文件配置

1logging.config=classpath:logback.xml 2 3spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver 4spring.datasource.url = jdbc:mysql://localhost:3306/data?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai 5spring.datasource.username = tim 6spring.datasource.password = 123456 7 8 9spring.jpa.database=MySQL 10spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 11spring.jpa.show-sql=true 12spring.jpa.hibernate.ddl-auto=update 13 14spring.datasource.hikari.maximum-pool-size=20 15spring.datasource.hikari.minimum-idle=5

7.3 shiro缓存配置

1<?xml version="1.0" encoding="UTF-8"?> 2<ehcache name="mycache-manager" updateCheck="false"> 3 4 <!-- 磁盘缓存位置 --> 5 <diskStore path="java.io.tmpdir"/> 6 7 <!-- 默认缓存 --> 8 <defaultCache 9 maxEntriesLocalHeap="1000" 10 eternal="false" 11 timeToIdleSeconds="3600" 12 timeToLiveSeconds="3600" 13 overflowToDisk="false"> 14 </defaultCache> 15 16 <cache name="shiro" 17 maxEntriesLocalHeap="2000" 18 eternal="false" 19 timeToIdleSeconds="60" 20 timeToLiveSeconds="120" 21 overflowToDisk="false" 22 diskPersistent="true"> 23 </cache> 24</ehcache>

7.4 实体类

用户类:

1import javax.persistence.Column; 2import javax.persistence.Entity; 3import javax.persistence.FetchType; 4import javax.persistence.GeneratedValue; 5import javax.persistence.GenerationType; 6import javax.persistence.Id; 7import javax.persistence.JoinColumn; 8import javax.persistence.JoinTable; 9import javax.persistence.ManyToMany; 10import javax.persistence.Table; 11import java.util.List; 12 13@Entity 14@Table(name = "user") 15public class User { 16 @Id 17 @GeneratedValue(strategy= GenerationType.AUTO) 18 private Integer id; 19 @Column(unique = true,length = 20) 20 private String username; 21 @Column(length = 64) 22 private String password; 23 private String salt; 24 @ManyToMany(fetch = FetchType.EAGER) 25 @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = {@JoinColumn(name = "rid") }) 26 private List<Role> roles; 27 28 public Integer getId() { 29 return id; 30 } 31 32 public void setId(Integer id) { 33 this.id = id; 34 } 35 36 public String getUsername() { 37 return username; 38 } 39 40 public void setUsername(String username) { 41 this.username = username; 42 } 43 44 public String getPassword() { 45 return password; 46 } 47 48 public void setPassword(String password) { 49 this.password = password; 50 } 51 52 public String getSalt() { 53 return salt; 54 } 55 56 public void setSalt(String salt) { 57 this.salt = salt; 58 } 59 60 public List<Role> getRoles() { 61 return roles; 62 } 63 64 public void setRoles(List<Role> roles) { 65 this.roles = roles; 66 } 67 68 public String getCredentialsSalt() { 69 return username + salt + salt; 70 } 71 72 @Override 73 public String toString() { 74 return "User [id=" + id + ", username=" + username + "]"; 75 } 76 77}

角色类:

1import javax.persistence.Entity; 2import javax.persistence.FetchType; 3import javax.persistence.GeneratedValue; 4import javax.persistence.Id; 5import javax.persistence.JoinColumn; 6import javax.persistence.JoinTable; 7import javax.persistence.ManyToMany; 8import javax.persistence.Table; 9import java.util.List; 10 11@Entity 12@Table(name = "role") 13public class Role { 14 @Id 15 @GeneratedValue 16 private Integer id; 17 18 private String role; 19 20 @ManyToMany(fetch = FetchType.EAGER) 21 @JoinTable(name = "role_permission", joinColumns = { @JoinColumn(name = "rid") }, inverseJoinColumns = {@JoinColumn(name = "pid") }) 22 private List<Permission> permissions; 23 24 @ManyToMany 25 @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "rid") }, inverseJoinColumns = {@JoinColumn(name = "uid") }) 26 private List<User> users; 27 28 public Integer getId() { 29 return id; 30 } 31 32 public void setId(Integer id) { 33 this.id = id; 34 } 35 36 public String getRole() { 37 return role; 38 } 39 40 public void setRole(String role) { 41 this.role = role; 42 } 43 44 public List<Permission> getPermissions() { 45 return permissions; 46 } 47 48 public void setPermissions(List<Permission> permissions) { 49 this.permissions = permissions; 50 } 51 52 public List<User> getUsers() { 53 return users; 54 } 55 56 public void setUsers(List<User> users) { 57 this.users = users; 58 } 59 60}

权限类:

1import javax.persistence.Entity; 2import javax.persistence.GeneratedValue; 3import javax.persistence.Id; 4import javax.persistence.JoinColumn; 5import javax.persistence.JoinTable; 6import javax.persistence.ManyToMany; 7import javax.persistence.Table; 8import java.util.List; 9 10@Entity 11@Table(name = "permission") 12public class Permission { 13 14 @Id 15 @GeneratedValue 16 private Integer id; 17 private String name; 18 @ManyToMany 19 @JoinTable(name = "role_permission", joinColumns = { @JoinColumn(name = "pid") }, inverseJoinColumns = {@JoinColumn(name = "rid") }) 20 private List<Role> roles; 21 22 public Integer getId() { 23 return id; 24 } 25 26 public void setId(Integer id) { 27 this.id = id; 28 } 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 public List<Role> getRoles() { 39 return roles; 40 } 41 42 public void setRoles(List<Role> roles) { 43 this.roles = roles; 44 } 45}

7.5 Repository

1import org.springframework.data.repository.CrudRepository; 2import vip.mycollege.mysql.jpa.entity.User; 3 4public interface UserRepository extends CrudRepository<User,String> { 5 User findUserByUsername(String username); 6}

7.6 Realm

1import org.apache.shiro.authc.AuthenticationException; 2import org.apache.shiro.authc.AuthenticationInfo; 3import org.apache.shiro.authc.AuthenticationToken; 4import org.apache.shiro.authc.SimpleAuthenticationInfo; 5import org.apache.shiro.authz.AuthorizationInfo; 6import org.apache.shiro.authz.SimpleAuthorizationInfo; 7import org.apache.shiro.realm.AuthorizingRealm; 8import org.apache.shiro.subject.PrincipalCollection; 9import org.apache.shiro.util.ByteSource; 10import vip.mycollege.mysql.jpa.entity.Permission; 11import vip.mycollege.mysql.jpa.entity.Role; 12import vip.mycollege.mysql.jpa.entity.User; 13import vip.mycollege.mysql.jpa.repository.UserRepository; 14 15import javax.annotation.Resource; 16 17public class UserPassRealm extends AuthorizingRealm { 18 19 @Resource 20 private UserRepository userRepository; 21 22 /** 23 * 登录 24 */ 25 @Override 26 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 27 String username = (String) token.getPrincipal(); 28 User user = userRepository.findUserByUsername(username);//从数据库查找username的用户 29 if (user == null) { 30 return null; 31 } 32 ByteSource salt = ByteSource.Util.bytes(user.getCredentialsSalt()); 33 String realmName = this.getClass().getName(); 34// 有盐值的认证 35 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), salt, realmName); 36// SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),realmName); 37 return authenticationInfo; 38 } 39 40 /** 41 * 授权 42 */ 43 @Override 44 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 45 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 46 String username = (String) principals.getPrimaryPrincipal(); 47 User user = userRepository.findUserByUsername(username);//从数据库获取权限信息 48 if(user == null){ 49 return null; 50 } 51 52 for (Role role : user.getRoles()) { 53 authorizationInfo.addRole(role.getRole()); 54 for (Permission permission : role.getPermissions()) { 55 authorizationInfo.addStringPermission(permission.getName()); 56 } 57 } 58 return authorizationInfo; 59 } 60 61}

7.7 配置类

1import com.google.common.io.Resources; 2import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 3import org.apache.shiro.authz.Authorizer; 4import org.apache.shiro.authz.ModularRealmAuthorizer; 5import org.apache.shiro.cache.ehcache.EhCacheManager; 6import org.apache.shiro.codec.Base64; 7import org.apache.shiro.config.ConfigurationException; 8import org.apache.shiro.crypto.hash.Sha256Hash; 9import org.apache.shiro.mgt.SecurityManager; 10import org.apache.shiro.realm.Realm; 11import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 12import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 13import org.apache.shiro.web.mgt.CookieRememberMeManager; 14import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 15import org.apache.shiro.web.servlet.SimpleCookie; 16import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 17import org.springframework.beans.factory.annotation.Qualifier; 18import org.springframework.context.annotation.Bean; 19import org.springframework.context.annotation.Configuration; 20import vip.mycollege.shiro.CredentialMatcher; 21import vip.mycollege.shiro.OnlineSessionFilter; 22import vip.mycollege.shiro.UserPassRealm; 23 24import javax.servlet.Filter; 25import java.net.URL; 26import java.util.HashMap; 27import java.util.LinkedHashMap; 28import java.util.Map; 29 30@Configuration 31public class ShiroConfig { 32 33 @Bean 34 public Authorizer authorizer() { 35 return new ModularRealmAuthorizer(); 36 } 37 38 @Bean 39 public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { 40 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 41 Map<String, String> filterChainDefinitionMap = new HashMap<>(); 42 43 // 必须设置 SecurityManager 44 shiroFilterFactoryBean.setSecurityManager(securityManager); 45 // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 46 shiroFilterFactoryBean.setLoginUrl("/shiro/login"); 47 // 登录成功后要跳转的链接 48 shiroFilterFactoryBean.setSuccessUrl("/shiro/index"); 49 // 访问未授权页面之后跳转链接 50 shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/unauthc"); 51 52 Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); 53 filters.put("onlineSession", new OnlineSessionFilter()); 54 shiroFilterFactoryBean.setFilters(filters);//设置自定义过滤器 55 56 // 所有请求需要认证 57 filterChainDefinitionMap.put("/**", "onlineSession"); 58 59 filterChainDefinitionMap.put("/*", "anon"); 60 filterChainDefinitionMap.put("/shiro/index", "authc"); 61 filterChainDefinitionMap.put("/shiro/admin", "roles[admin]"); 62 filterChainDefinitionMap.put("/shiro/insertUpdate", "perms[printer:insert,update]"); 63 filterChainDefinitionMap.put("/shiro/delete", "perms[printer:delete]"); 64 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 65 return shiroFilterFactoryBean; 66 } 67 68 @Bean 69 public HashedCredentialsMatcher hashedCredentialsMatcher() { 70 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); 71 hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME); // 散列算法 72 hashedCredentialsMatcher.setHashIterations(3); // 散列次数 73 return hashedCredentialsMatcher; 74 } 75 76 /** 77 * 缓存管理器 使用Ehcache实现 78 */ 79 @Bean 80 public EhCacheManager getEhCacheManager() { 81// 先查找是否已经创建了缓存管理器 82 net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("shiro-ehcache"); 83 if(cacheManager == null){//如果没有就在classpath下查找shiro-ehcache.xml配置文件创建缓存管理器 84 URL url = Resources.getResource("shiro-ehcache.xml"); 85 if(url == null){ 86 url = Resources.getResource("ehcache/shiro-ehcache.xml"); 87 } 88 if(url == null){ 89 throw new ConfigurationException("没有找到shiro-ehcache.xml配置文件"); 90 } 91 cacheManager = net.sf.ehcache.CacheManager.create(url); 92 } 93 EhCacheManager em = new EhCacheManager(); 94 em.setCacheManager(cacheManager); 95 return em; 96 } 97 98 @Bean("userPassRealm") 99 public UserPassRealm shiroRealm(EhCacheManager cacheManager) {//配置自定义的权限登录器 100 UserPassRealm shiroRealm = new UserPassRealm(); 101 shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); 102 shiroRealm.setCacheManager(cacheManager); 103 shiroRealm.setAuthorizationCacheName("shiro"); 104// shiroRealm.setCredentialsMatcher(credentialMatcher()); 105 return shiroRealm; 106 } 107 108 @Bean("securityManager") 109 public SecurityManager securityManager(@Qualifier("userPassRealm") Realm realm) {//配置核心安全事务管理器 110 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 111 securityManager.setRealm(realm); 112 securityManager.setRememberMeManager(rememberMeManager()); 113 return securityManager; 114 } 115 116 /** 117 * cookie 属性设置 118 */ 119 public SimpleCookie rememberMeCookie() { 120 SimpleCookie cookie = new SimpleCookie("rememberMe"); 121 cookie.setDomain("www.mycollege.vip"); 122 cookie.setPath("/"); 123 cookie.setHttpOnly(true); 124 cookie.setMaxAge(7 * 24 * 60 * 60); 125 return cookie; 126 } 127 128 /** 129 * 记住我 130 */ 131 public CookieRememberMeManager rememberMeManager() { 132 CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); 133 cookieRememberMeManager.setCookie(rememberMeCookie()); 134 cookieRememberMeManager.setCipherKey(Base64.decode("xxxxxx")); 135 return cookieRememberMeManager; 136 } 137 138 @Bean("credentialMatcher") 139 public CredentialMatcher credentialMatcher() { //配置自定义的密码比较器 140 return new CredentialMatcher(); 141 } 142 143 /** 144 * 开启注解支持,方便使用: 145 * 146 * @RequiresPermissions 147 * @RequiresAuthentication 148 * @RequiresUser 149 * @RequiresGuest 150 * @RequiresRoles 151 */ 152 @Bean 153 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) { 154 AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); 155 advisor.setSecurityManager(securityManager); 156 return advisor; 157 } 158 159 @Bean 160 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { 161 DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); 162 creator.setProxyTargetClass(true); 163 return creator; 164 } 165}

7.8 Controller

1import org.apache.shiro.SecurityUtils; 2import org.apache.shiro.authc.IncorrectCredentialsException; 3import org.apache.shiro.authc.UnknownAccountException; 4import org.apache.shiro.authc.UsernamePasswordToken; 5import org.apache.shiro.authz.annotation.Logical; 6import org.apache.shiro.authz.annotation.RequiresAuthentication; 7import org.apache.shiro.authz.annotation.RequiresGuest; 8import org.apache.shiro.authz.annotation.RequiresPermissions; 9import org.apache.shiro.authz.annotation.RequiresRoles; 10import org.apache.shiro.authz.annotation.RequiresUser; 11import org.apache.shiro.crypto.RandomNumberGenerator; 12import org.apache.shiro.crypto.SecureRandomNumberGenerator; 13import org.apache.shiro.crypto.hash.Sha256Hash; 14import org.apache.shiro.crypto.hash.SimpleHash; 15import org.apache.shiro.subject.Subject; 16import org.apache.shiro.util.ByteSource; 17import org.springframework.web.bind.annotation.GetMapping; 18import org.springframework.web.bind.annotation.RequestMapping; 19import org.springframework.web.bind.annotation.RequestParam; 20import org.springframework.web.bind.annotation.RestController; 21import vip.mycollege.mysql.jpa.entity.User; 22import vip.mycollege.mysql.jpa.repository.UserRepository; 23 24import javax.annotation.Resource; 25 26 27@RestController 28@RequestMapping("/shiro") 29public class ShiroController { 30 31 private static final RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); 32 33 @Resource 34 private UserRepository userRepository; 35 36 @GetMapping("/index") 37 public String index() { 38 Subject subject = SecurityUtils.getSubject(); 39 User user = (User) subject.getSession().getAttribute("user"); 40 return user.toString(); 41 } 42 43 @GetMapping("/admin") 44 public String admin() { 45 return "管理员页面"; 46 } 47 48 @GetMapping("/delete") 49 public Object delete() { 50 return "需要删除相关权限页面"; 51 } 52 53 @GetMapping("/insert-update") 54 public Object insertUpdate() { 55 return "需要插入更新相关权限页面"; 56 } 57 58 59 @GetMapping("/login") 60 public String login() { 61 return "登录页面"; 62 } 63 64 @GetMapping("/unauthc") 65 public String unauthc() { 66 return "该页面您还未获授权,暂时不能访问"; 67 } 68 69 @GetMapping("/do-login") 70 public Object doLogin(@RequestParam String username, @RequestParam String password) { 71 UsernamePasswordToken token = new UsernamePasswordToken(username, password); 72 // 记住我功能 73// UsernamePasswordToken token = new UsernamePasswordToken(username, password,true); 74 75 //设置session 76 //SecurityUtils.getSubject().getSession().setAttribute("deployEnv", deployEnv); 77 78 //如果用户已登录,先踢出 79 //ShiroSecurityHelper.kickOutUser(username)); 80 Subject subject = SecurityUtils.getSubject(); 81 try { 82 subject.login(token); 83 } catch (IncorrectCredentialsException ice) { 84 return "password error!"; 85 } catch (UnknownAccountException uae) { 86 return "username error!"; 87 } 88 89 User user = userRepository.findUserByUsername(username); 90 subject.getSession().setAttribute("user", user); 91 return "登录成功"; 92 } 93 94 @GetMapping("/register") 95 public Object register(@RequestParam("username") String username, @RequestParam("password") String password) { 96 User user = new User(); 97 user.setUsername(username); 98 user.setPassword(password); 99 user.setSalt(randomNumberGenerator.nextBytes().toHex()); 100 ByteSource byteSourceSalt = ByteSource.Util.bytes(user.getCredentialsSalt()); 101 SimpleHash simpleHash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, byteSourceSalt, 3); 102 String newPassword = simpleHash.toHex(); 103 user.setPassword(newPassword); 104 userRepository.save(user); 105 return "注册成功"; 106 } 107 108 @RequiresPermissions({"printer", "camera"})//默认and 109 @GetMapping("/requiresPermissions") 110 public String requiresPermissions(){ 111 return "ok"; 112 } 113 114 @RequiresRoles(value={"admin","sys"},logical= Logical.OR) 115 @GetMapping("/requiresRoles") 116 public String requiresRoles(){ 117 return "ok"; 118 } 119 120 @RequiresGuest 121 @GetMapping("/requiresGuest") 122 public String requiresGuest(){ 123 return "ok"; 124 } 125 126 @RequiresUser 127 @GetMapping("/requiresUser") 128 public String requiresUser(){ 129 return "ok"; 130 } 131 132 @RequiresAuthentication 133 @GetMapping("/requiresAuthentication") 134 public String requiresAuthentication(){ 135 return "ok"; 136 } 137}

7.9 启动类

1import org.springframework.boot.SpringApplication; 2import org.springframework.boot.autoconfigure.SpringBootApplication; 3 4@SpringBootApplication 5public class StartApplication { 6 public static void main(String[] args) { 7 SpringApplication.run(StartApplication.class, args); 8 } 9}

启动之后,会自动创建表,当然数据库配置要换成你自己的,没有的过滤器可以删除掉。

然后,在数据库中添加一点角色和权限数据就可以通过Controller进行测试了。

8. 测试

1# 注册2个用户 2http://localhost:8080/shiro/register?username=tim&password=123456 3http://localhost:8080/shiro/register?username=bob&password=123456 4 5# 游客访问 6http://localhost:8080/shiro/requiresGuest 7 8# 没有权限直接抛出异常 9http://localhost:8080/shiro/requiresPermissions 10 11# 检查角色,登录之后有权限执行 12http://localhost:8080/shiro/requiresRoles 13 14# 登录或者记住我 15http://localhost:8080/shiro/requiresUser 16 17# 必须是登录用户,用于一下敏感页面 18http://localhost:8080/shiro/requiresAuthentication 19 20# 登录 21http://localhost:8080/shiro/do-login?username=tim&password=123456

更多的校验可以使用前面的实例自己测试,需要添加user、permssion、role相关数据。

当然,你也可以通过下面的测试类来进行测试:

1import org.junit.Before; 2import org.junit.Test; 3import org.junit.runner.RunWith; 4import org.springframework.beans.factory.annotation.Autowired; 5import org.springframework.boot.test.context.SpringBootTest; 6import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7import org.springframework.test.context.web.WebAppConfiguration; 8import org.springframework.test.web.servlet.MockMvc; 9import org.springframework.test.web.servlet.MvcResult; 10import org.springframework.test.web.servlet.ResultHandler; 11import org.springframework.web.context.WebApplicationContext; 12 13import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 14import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 15import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 16 17@RunWith(SpringJUnit4ClassRunner.class) 18@WebAppConfiguration 19@SpringBootTest 20public class ShiroControllerTest { 21 22 @Autowired 23 protected WebApplicationContext wac; 24 25 protected MockMvc mockMvc; 26 27 @Before 28 public void setUp(){ 29 this.mockMvc = webAppContextSetup(this.wac).build(); 30 } 31 32 @Test 33 public void index() throws Exception { 34 mockMvc.perform(get("/shiro/index")) 35 .andExpect(status().isOk()) 36 .andDo(new ResultHandler() { 37 @Override 38 public void handle(MvcResult result) throws Exception { 39 System.out.println(result.getResponse().getContentAsString()); 40 } 41 }) 42 .andReturn(); 43 } 44 45 @Test 46 public void admin() { 47 } 48 49 @Test 50 public void delete() { 51 } 52 53 @Test 54 public void insertUpdate() { 55 } 56 57 @Test 58 public void login() throws Exception { 59 mockMvc.perform(get("/shiro/login") 60 .param("username", "tim") 61 .param("password", "123456") 62 ) 63 .andExpect(status().isOk()) 64 .andDo(new ResultHandler() { 65 @Override 66 public void handle(MvcResult result) throws Exception { 67 System.out.println(result.getResponse().getContentAsString()); 68 } 69 }) 70 .andReturn(); 71 } 72 73 @Test 74 public void unauthc() { 75 } 76 77 @Test 78 public void doLogin() { 79 } 80 81 @Test 82 public void register() throws Exception { 83 mockMvc.perform(get("/shiro/register") 84 .param("username", "allen") 85 .param("password", "123456") 86 ) 87 .andExpect(status().isOk()) 88 .andDo(new ResultHandler() { 89 @Override 90 public void handle(MvcResult result) throws Exception { 91 System.out.println(result.getResponse().getContentAsString()); 92 } 93 }) 94 .andReturn(); 95 } 96 97 @Test 98 public void requiresPermissions() { 99 } 100 101 @Test 102 public void requiresRoles() { 103 } 104 105 @Test 106 public void requiresGuest() { 107 } 108 109 @Test 110 public void requiresUser() { 111 } 112 113 @Test 114 public void requiresAuthentication() { 115 } 116}

9. 权限

shiro的角色很好理解,但是权限比较难理解,所以放在最后来简单介绍一下。

首先:Subject有一个检查权限的方法:

boolean isPermitted(String permission);

具体实现是在DelegatingSubject:

1public boolean isPermitted(String permission) { 2 return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission); 3}

继续跟进AuthorizingSecurityManager的实现:

1public boolean isPermitted(PrincipalCollection principals, String permissionString) { 2 return this.authorizer.isPermitted(principals, permissionString); 3}

我们可以看到是通过Authorizer授权器来实现,以AuthorizingRealm为例的实现:

1public boolean isPermitted(PrincipalCollection principals, String permission) { 2 Permission p = getPermissionResolver().resolvePermission(permission); 3 return isPermitted(principals, p); 4}

可以看到是先获取到一个PermissionResolver,然后把字符串的permssion解析为Permission接口,然后通过Permission接口去校验。

AuthorizingRealm默认实现为解析出WildCardPermission,所以如果我们不想自己实现权限校验逻辑,就可以使用WildCardPermission。

问题的关键就是:WildCardPermission怎样校验权限?

10. WildCardPermission权限

DomainPermission有一个之类DomainPermission。

权限使用字符串定义,分三级,冒号分开:

资源(resource,domain):操作(action):实例(instance,操作在那其上面执行)

permission

说明

printer

所有实例都有资源printer的所有权限,等价于printer::

printer:query

资源上面指定query权限,等价于printer:query.*

printer:print,query

资源上面指定多个权限

printer:*

资源上面所用权限

*:view

所有资源都有view权限

printer:query:inst32

指定实例才有query权限

printer:print:*

所有实例都有print权限

printer::

所有实例都有资源printer的所有权限

printer:*:inst32

inst32实例有资源printer的所有权限

printer:query,print:inst32

inst32实例有资源printer的query和print权限

省略只能省略最右边的,资源必须指定。

WildCardPermission权限字符串都是你自己定义设计的,WildCardPermission只会去对比检查权限是否匹配,而并不关心权限的字符串是什么。

11. 文档资料

shiro官网 10分钟小实例 shiro guide shiro文档 shiro web shiro授权 shiro权限

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang