用户权限管理一般是对用户页面、按钮的访问权限管理。Shiro框架是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理,对于Shiro的介绍这里就不多说。本篇博客主要是了解Shiro的基础使用方法,在权限管理系统中集成Shiro实现登录、url和页面按钮的访问控制。
使用SpringBoot集成Shiro时,在pom.xml中可以引入shiro-spring-boot-web-starter。由于使用的是thymeleaf框架,thymeleaf与Shiro结合需要 引入thymeleaf-extras-shiro。
????????
????????????org.apache.shiro
????????????shiro-spring-boot-web-starter
????????????1.4.0
????????
????????
????????
????????????com.github.theborakompanioni
????????????thymeleaf-extras-shiro
????????????2.0.0
????????
有哪些url是需要拦截的,哪些是不需要拦截的,登录页面、登录成功页面的url、自定义的Realm等这些信息需要设置到Shiro中,所以创建Configuration文件ShiroConfig。
package?com.example.config;
import?org.apache.shiro.mgt.SecurityManager;
import?org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import?org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import?org.springframework.beans.factory.annotation.Qualifier;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import?java.util.LinkedHashMap;
import?java.util.Map;
@Configuration
public?class?ShiroConfig?{
????@Bean("shiroFilterFactoryBean")
????public?ShiroFilterFactoryBean?shiroFilterFactoryBean(SecurityManager?securityManager)?{
????????System.out.println("ShiroConfiguration.shirFilter()");
????????ShiroFilterFactoryBean?shiroFilterFactoryBean?=?new?ShiroFilterFactoryBean();
????????shiroFilterFactoryBean.setSecurityManager(securityManager);
????????//拦截器.
????????Map?filterChainDefinitionMap?=?new?LinkedHashMap();
????????//?配置不会被拦截的链接?顺序判断
????????filterChainDefinitionMap.put("/static/**",?"anon");
????????//配置退出?过滤器,其中的具体的退出代码Shiro已经替我们实现了
????????filterChainDefinitionMap.put("/logout",?"logout");
????????//:这是一个坑呢,一不小心代码就不好使了;
????????//
????????filterChainDefinitionMap.put("/**",?"authc");
????????//?如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
????????shiroFilterFactoryBean.setLoginUrl("/login");
????????//?登录成功后要跳转的链接
????????shiroFilterFactoryBean.setSuccessUrl("/index");
????????//未授权界面;
????????shiroFilterFactoryBean.setUnauthorizedUrl("/403");
????????shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
????????return?shiroFilterFactoryBean;
????}
????@Bean(name="defaultWebSecurityManager")????//创建DefaultWebSecurityManager????
????public?DefaultWebSecurityManager?getDefaultWebSecurityManager(@Qualifier("userRealm")MyShiroRealm?userRealm){????????
????????DefaultWebSecurityManager?defaultWebSecurityManager?=?new?DefaultWebSecurityManager();????????
????????defaultWebSecurityManager.setRealm(userRealm);????????
????????return?defaultWebSecurityManager;
????}????????
????//创建Realm????
????@Bean(name="userRealm")????
????public?MyShiroRealm?getUserRealm(){????????
????????return?new?MyShiroRealm();????
????}
????@Bean
????public?ShiroDialect?shiroDialect()?{
????????return?new?ShiroDialect();
????}
}
ShiroDialect这个bean对象是在thymeleaf与Shiro结合,前端html访问Shiro时使用。
在自定义的Realm中继承了AuthorizingRealm抽象类,重写了两个方法:doGetAuthorizationInfo和doGetAuthenticationInfo。doGetAuthorizationInfo主要是用来处理权限配置,doGetAuthenticationInfo主要处理身份认证。
这里在doGetAuthorizationInfo中,将role表的id和permission表的code分别设置到SimpleAuthorizationInfo对象中的role和permission中。
还有一个地方需要注意:@Component(“authorizer”),刚开始我没设置,但报错提示需要一个authorizer的bean,查看AuthorizingRealm可以发现它implements了Authorizer,所以在自定义的realm上添加@Component(“authorizer”)就可以了。
package?com.example.config;
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.realm.AuthorizingRealm;
import?org.apache.shiro.subject.PrincipalCollection;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.stereotype.Component;
import?com.example.pojo.Permission;
import?com.example.pojo.Role;
import?com.example.pojo.User;
import?com.example.service.RoleService;
import?com.example.service.UserService;
@Component("authorizer")
public?class?MyShiroRealm?extends?AuthorizingRealm?{
????@Autowired
????private?UserService?userService;
????@Autowired
????private?RoleService?roleService;
????@Override
????protected?AuthorizationInfo?doGetAuthorizationInfo(PrincipalCollection?principals)?{
????????System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
????????SimpleAuthorizationInfo?authorizationInfo?=?new?SimpleAuthorizationInfo();
????????User?user??=?(User)principals.getPrimaryPrincipal();
????????System.out.println("User:"+user.toString()+"?roles?count:"+user.getRoles().size());
????????for(Role?role:user.getRoles()){
????????????authorizationInfo.addRole(role.getId());
????????????role=roleService.getRoleById(role.getId());
????????????System.out.println("Role:"+role.toString());
????????????for(Permission?p:role.getPermissions()){
????????????????System.out.println("Permission:"+p.toString());
????????????????authorizationInfo.addStringPermission(p.getCode());
????????????}
????????}
????????System.out.println("权限配置-->authorizationInfo"+authorizationInfo.toString());
????????return?authorizationInfo;
????}
????/*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
????@Override
????protected?AuthenticationInfo?doGetAuthenticationInfo(AuthenticationToken?token)
????????????throws?AuthenticationException?{
????????System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
????????//获取用户的输入的账号.
????????String?username?=?(String)token.getPrincipal();
????????System.out.println(token.getCredentials());
????????//通过username从数据库中查找?User对象,如果找到,没找到.
????????//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
????????User?user?=?userService.getUserById(username);
????????System.out.println("----->>userInfo="+user);
????????if(user?==?null){
????????????return?null;
????????}
????????SimpleAuthenticationInfo?authenticationInfo?=?new?SimpleAuthenticationInfo(
????????????????user,?//用户名
????????????????"123456",?//密码
????????????????getName()??//realm?name
????????);
????????return?authenticationInfo;
????}
}
这里做了一个非常丑的登录页面,主要是自己懒,不想在网上复制粘贴找登录页面了。
????
????
????
????
????
????
????
????