将Shiro集成到Spring-Boot工程中

将Shiro集成到Spring-Boot工程中

​ Shiro官网文档:

https://shiro.apache.org/spring-boot.html

导入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.6.0</version>
</dependency>

编写自定义Realm

public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    /**
     * @MethodName doGetAuthorizationInfo
     * @Description 权限配置类   拦截当前登录的用户权限
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //查询用户名称
        User user = userService.getUserByName(name);
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (User.Role role : user.getRoles()) {
            //添加角色
            simpleAuthorizationInfo.addRole(role.getRoleName());

            //添加权限
            for (User.Role.Permissions permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
            }
        }
        return simpleAuthorizationInfo;
    }

    /**
     * @MethodName doGetAuthenticationInfo
     * @Description 认证配置类  拦截登录
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
            User user = userService.getUserByName(name);
        if (user == null) {
            //这里返回后会报出对应异常
            return null;
        } else {
            //这里验证authenticationToken和simpleAuthenticationInfo的信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassWord(), getName());
            return simpleAuthenticationInfo;
        }
    }
}

Shiro配置类

@Configuration
public class ShiroConfig {

	// 配置自定义Realm
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(credentialsMatcher()); //配置使用哈希密码匹配
        return userRealm;
    }

	// 配置url过滤器
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        chainDefinition.addPathDefinition("/captcha", "anon");
        chainDefinition.addPathDefinition("/logout","anon");
        chainDefinition.addPathDefinition("/layuiadmin/**", "anon");
        chainDefinition.addPathDefinition("/druid/**", "anon");
        chainDefinition.addPathDefinition("/api/**", "anon");
        // all other paths require a logged in user
        chainDefinition.addPathDefinition("/login","anon");
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    // 设置用于匹配密码的CredentialsMatcher
    @Bean
    public HashedCredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);  // 散列算法,这里使用更安全的sha256算法
        credentialsMatcher.setStoredCredentialsHexEncoded(false);  // 数据库存储的密码字段使用HEX还是BASE64方式加密
        credentialsMatcher.setHashIterations(1024);  // 散列迭代次数
        return credentialsMatcher;
    }
 }

通过阅读shiro-spring-boot-starter依赖中的ShiroAutoConfiguration类的源码可以得知

springboot内会自动注册Realm类以及SecurityManager,并且SecurityManager会自动将这些Realm管理起来,因此我们不需要执行SecurityManager.setRealms(realms)
只需要将自定义的MyRealm注入到spring容器即可。

如果我们不自己注册Realm到容器中,则会抛出 NoRealmBeanConfiguredException异常

同时springboot会自动注册ShiroFilterFactoryBean,并将spring容器中的ShiroFilterChainDefinitionSecurityManager管理起来,
因此我们也不需要执行filterFactoryBean.setFilterChainDefinitionMap(map)filterFactoryBean.setSecurityManager(this.securityManager);
我们只需要将ShiroFilterChainDefinition注入到spring容器即可。

注意:springboot虽然已经注册了默认的ShiroFilterChainDefinition,但url过滤器默认只配置了chainDefinition.addPathDefinition(" /** ", “authc”);,在日常开发中,光这一条url配置应该是不够的,因此通常我们需要自定义这个bean。

soringboot还会自动将容器中的Filter过滤器由ShiroFilterFactoryBean管理,所以当我们需要自定义权限管理过滤器时,只需要继承FormAuthenticationFilter类,并添加自己的认证逻辑,如验证码等,然后将自定义的过滤器注入容器即可,无需执行filterFactoryBean.setFilters(filterMap);

自定义认证过滤器


@WebFilter
public class MyFormAuthenticationFilter extends FormAuthenticationFilter{
	@Override  
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {  
        // 在这里进行验证码的校验  
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;  
        HttpSession session = httpServletRequest.getSession();  
  
        // 取出验证码  
        String validateCode = (String) session.getAttribute("validateCode");  
        // 取出页面的验证码  
        // 输入的验证和session中的验证进行对比  
        String randomcode = httpServletRequest.getParameter("code");  
        if (randomcode != null && validateCode != null && !randomcode.equals(validateCode)) {  
            // 如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中  
            httpServletRequest.setAttribute("shiroLoginFailure", "kaptchaValidateFailed");//自定义登录异常  
            // 拒绝访问,不再校验账号和密码  
            return true;  
        }  
        return super.onAccessDenied(request, response);  
    }  
}

Shiro默认登录页为/login.jsp,需要在项目配置文件application.yml中修改默认登录页等配置

shiro:
  loginUrl: /login
  successUrl: /
  unauthorizedUrl: /error/error

Controller请求

@RestController
public class UserController {

    @GetMapping("login")
    public String login(User user) {
        if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassWord())) {
            return "请输入用户名和密码!";
        }
        //用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                user.getPassWord()
        );
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");
        } catch (UnknownAccountException e) {
            log.error("用户名不存在!", e);
            return "用户名不存在!";
        } catch (AuthenticationException e) {
            log.error("账号或密码错误!", e);
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            log.error("没有权限!", e);
            return "没有权限";
        }
        return "login success";
    }
    @RequiresPermissions("query")
    @GetMapping("/index")
    public String index() {
        return "query success!";
    }

    @RequiresPermissions("add")
    @GetMapping("/add")
    public String add() {
        return "add success!";
    }

Shiro默认过滤器

配置缩写对应的过滤器功能
anonAnonymousFilter指定url可以匿名访问
authcFormAuthenticationFilter指定url需要登录认证,默认会从请求中获取usernamepassword,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们可以通过继承此类来扩展我们的判断逻辑,如判断验证码等。
authcBasicBasicHttpAuthenticationFilter指定url需要basic登录
logoutLogoutFilter登出过滤器,配置指定url就可以实现退出功能,非常方便
noSessionCreationNoSessionCreationFilter禁止创建会话
permsPermissionsAuthorizationFilter需要指定权限才能访问
portPortFilter需要指定端口才能访问
restHttpMethodPermissionFilter将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
rolesRolesAuthorizationFilter需要指定角色才能访问
sslSslFilter需要https请求才能访问
userUserFilter需要已登录或“记住我”的用户才能访问

例:

 //访问/home请求需要登录认证
 chainDefinition.addPathDefinition("/home", "anon");    
  //访问/manager请求需要manager角色
 chainDefinition.addPathDefinition("/manager", "roles[manager]"); 
  //访问/manager/delate请求需要admin权限
 chainDefinition.addPathDefinition("/manager/delate", "perms[admin]");

Shiro常用的权限控制注解

可以在Controller类上使用

注解功能
@RequiresGuest只有游客可以访问
@RequiresAuthentication需要登录才能访问
@RequiresUser已登录的用户或“记住我”的用户能访问
@RequiresRoles已登录的用户需具有指定的角色才能访问
@RequiresPermissions已登录的用户需具有指定的权限才能访问

URL和注解组合使用

Shiro可以使用url配置控制权限,也可以在控制器类上使用注解控制权限。同时使用两种配置方式灵活结合,才是适应不同应用场景的最佳实践。只用注解或只用url配置,都不够灵活,有时会很麻烦。思路是:

用url配置控制鉴权,实现粗粒度控制;用注解控制授权,实现细粒度控制

@RequireXXX注解可能的Bug

注意:解决spring aop和注解配置一起使用的bug。如果您在使用shiro注解配置的同时,引入了spring aop 的starter,会有一个奇怪的问题,导致shiro注解的请求,不能被映射,需加入以下配置:

@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
    	/**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,		 * 导致返回404。 加入这项配置能解决这个bug
         */
    defaultAdvisorAutoProxyCreator.setUsePrefix(true);
    //defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
    return defaultAdvisorAutoProxyCreator;
}
注解权限

控制器上通过注解配置详细的角色和权限,多个权限和角色之间默认是“与”关系,可以通过logical参数设置为“或”。

@RequiresRoles( value = {"admin", "user"}, logical = Logical.OR)
@GetMapping("/getRoles")
public String getRoles() {
    return "getRoles success!";
}

缓存

启用缓存只需要提供提供CacheManager bean:

@Bean
protected CacheManager cacheManager() {
    return new MemoryConstrainedCacheManager();
}

Shiro-Spring配置属性

KeyDefault ValueDescription
shiro.enabledtrue启用Shiro的Spring模块
shiro.web.enabledtrue启用Shiro的Spring Web模块
shiro.annotations.enabledtrue为Shiro的注释启用Spring支持
shiro.sessionManager.deleteInvalidSessionstrue从会话存储中删除无效的会话
shiro.sessionManager.sessionIdCookieEnabledtrue启用会话ID到cookie,以进行会话跟踪
shiro.sessionManager.sessionIdUrlRewritingEnabledtrue启用会话URL重写支持
shiro.userNativeSessionManagerfalse如果启用,Shiro将管理HTTP会话而不是容器
shiro.sessionManager.cookie.nameJSESSIONID会话Cookie名称
shiro.sessionManager.cookie.maxAge-1会话Cookie的最大生命
shiro.sessionManager.cookie.domainnull会话Cookie域
shiro.sessionManager.cookie.pathnull会话Cookie路径
shiro.sessionManager.cookie.securefalse会话Cookie安全标志
shiro.rememberMeManager.cookie.namerememberMeRememberMe cookie名称
shiro.rememberMeManager.cookie.maxAgeone yearRememberMe Cookie的最大年龄
shiro.rememberMeManager.cookie.domainnullRememberMe Cookie域
shiro.rememberMeManager.cookie.pathnullRememberMe Cookie路径
shiro.rememberMeManager.cookie.securefalseRememberMe cookie安全标志
shiro.loginUrl/login.jsp未经身份验证的用户重定向到登录页面时使用的登录URL
shiro.successUrl/用户登录后的默认登录页面(如果在当前会话中找不到替代登录页面)
shiro.unauthorizedUrlnull用于将用户重定向到未经授权的页面(403错误)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值