在前后端分离项目中使用SpringBoot集成Shiro

本文转载自:https://www.cnblogs.com/sword-successful/p/11093803.html

前言

       这次在处理一个小项目时用到了前后端分离,服务端使用springboot2.x。权限验证使用了Shiro。前后端分离首先需要解决的是跨域问题,POST接口跨域时会预发送一个OPTIONS请求,浏览器收到响应后会继续执行POST请求。 前后端分离后为了保持会话状态使用session持久化插件shiro-redis,持久化session可以持久化到关系型数据库,也可以持久化到非关系型数据库(主要是重写SessionDao)。Shiro已提供了SessionDao接口和抽象类。如果项目中用到Swagger的话,还需要把swagger相关url放行。

搭建依赖

<dependency>

    <!--session持久化插件-->

    <groupId>org.crazycake</groupId>

    <artifactId>shiro-redis</artifactId>

    <version>3.2.3</version>

</dependency>

<dependency>

    <!--spring shiro依赖-->

    <groupId>org.apache.shiro</groupId>

    <artifactId>shiro-spring</artifactId>

    <version>1.4.1</version>

</dependency>


 Shiro权限配置

1、ShiroConfig。这里主要是shiro核心配置。比如SecurityManager、SessionManager、CacheManager。

public class ShiroConfig {

 

    @Value("${spring.redis.shiro.host}")

    private String host;

    @Value("${spring.redis.shiro.port}")

    private int port;

    @Value("${spring.redis.shiro.timeout}")

    private int timeout;

    @Value("${spring.redis.shiro.password}")

    private String password;

 

 

    /**

     * 权限规则配置

     **/

    @Bean

    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        shiroFilterFactoryBean.setSecurityManager(securityManager);

 

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();

        filters.put("authc"new MyFormAuthorizationFilter());

 

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

 

        //swagger资源不拦截

        filterChainDefinitionMap.put("/swagger-ui.html""anon");

        filterChainDefinitionMap.put("/swagger-resources/**/**""anon");

        filterChainDefinitionMap.put("/v2/api-docs""anon");

        filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**""anon");

        filterChainDefinitionMap.put("/configuration/security""anon");

        filterChainDefinitionMap.put("/configuration/ui""anon");

 

        filterChainDefinitionMap.put("/login/ajaxLogin""anon");

        filterChainDefinitionMap.put("/login/unauth""anon");

        filterChainDefinitionMap.put("/login/logout""anon");

        filterChainDefinitionMap.put("/login/register","anon");

        filterChainDefinitionMap.put("/**""authc");

 

        shiroFilterFactoryBean.setLoginUrl("/login/unauth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;

    }

 

 

    /**

     * shiro安全管理器(权限验证核心配置)

     **/

    @Bean

    public SecurityManager securityManager() {

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        securityManager.setRealm(myShiroRealm());

        securityManager.setSessionManager(sessionManager());

        securityManager.setCacheManager(cacheManager());

 

        return securityManager;

    }

 

    /**

     * 会话管理

     **/

    @Bean

    public SessionManager sessionManager() {

        MySessionManager sessionManager = new MySessionManager();

        sessionManager.setSessionIdUrlRewritingEnabled(false); //取消登陆跳转URL后面的jsessionid参数

        sessionManager.setSessionDAO(sessionDAO());

        sessionManager.setGlobalSessionTimeout(-1);//不过期

        return sessionManager;

    }

 

    /**

     * 使用的是shiro-redis开源插件 缓存依赖

     **/

    @Bean

    public RedisManager redisManager() {

        RedisManager redisManager = new RedisManager();

        redisManager.setHost(host+":"+port);

        redisManager.setTimeout(timeout);

        redisManager.setPassword(password);

        return redisManager;

    }

 

    /**

     * 使用的是shiro-redis开源插件 session持久化

     **/

    public RedisSessionDAO sessionDAO() {

        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();

        redisSessionDAO.setRedisManager(redisManager());

        return redisSessionDAO;

    }

 

 

    /**

     * 缓存管理

     **/

    @Bean

    public CacheManager cacheManager() {

        RedisCacheManager redisCacheManager = new RedisCacheManager();

        redisCacheManager.setRedisManager(redisManager());

        return redisCacheManager;

    }

 

 

    /**

     * 权限管理

     **/

    @Bean

    public MyShiroRealm myShiroRealm() {

 

        return new MyShiroRealm();

    }

}

2、MyShiroRealm 用户身份验证、自定义权限。

public class MyShiroRealm extends AuthorizingRealm {

 

    private Logger logger= LoggerFactory.getLogger(MyShiroRealm.class);

 

    @Resource

    UserDao userDao;

 

 

    @Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        logger.info("===================权限验证==================");

        return null;

    }

 

 

 

    @Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

 

        UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;

        User currentUser=userDao.findUser(token.getUsername());

        if(null == currentUser){

            throw new AuthenticationException("账户不存在");

        }

 

        if(!currentUser.getPassword().equals(new String(token.getPassword()))){

            throw new IncorrectCredentialsException("账户密码不正确");

        }

 

        if(currentUser.getIsdel()==1){

            throw new LockedAccountException("账户已冻结");

        }

 

        Subject subject = SecurityUtils.getSubject();

 

        BIUser biUser=new BIUser();

        biUser.setUserId(currentUser.getUserId());

        biUser.setOrgId(currentUser.getOrgid());

        biUser.setUserName(currentUser.getUsername());

        biUser.setPassword(currentUser.getPassword());

        biUser.setSessionId(subject.getSession().getId().toString());

        biUser.setIsdel(currentUser.getIsdel());

        biUser.setCreateTime(currentUser.getCreatetime());

 

        logger.info("======已授权"+biUser.toString()+"====");

 

        return new SimpleAuthenticationInfo(biUser,biUser.getPassword(),biUser.getUserName());

    }

}

3、MySessionManager。shiro权限验证是根据客户端Cookie中的JSESSIONID值来确定身份是否合格。前后端分离后这个地方需要处理。客户端调用服务端登陆接口,验证通过后返回给客户端一个token值(这里我放的是sessionid)。客户端保存token值,然后调用其他接口时把token值放在header中。对前端来说也就是放在ajax的headers参数中。

public class MySessionManager extends DefaultWebSessionManager {

 

    private static final String AUTHORIZATION = "Authorization";

 

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

 

    public MySessionManager() {

    }

 

    @Override

    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {

        //从前端ajax headers中获取这个参数用来判断授权

        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);

        if (StringUtils.hasLength(id)) {

            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);

            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);

            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);

            return id;

        else {

            //从前端的cookie中取值

            return super.getSessionId(request, response);

        }

 

    }

}

 4、MyFormAuthorizationFilter。对于跨域的POST请求,浏览器发起POST请求前都会发送一个OPTIONS请求已确定服务器是否可用,OPTIONS请求通过后继续执行POST请求,而shiro自带的权限验证是无法处理OPTIONS请求的,所以这里需要重写isAccessAllowed方法。

public class MyFormAuthorizationFilter extends FormAuthenticationFilter {

    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {

        HttpServletRequest httpServletRequest = WebUtils.toHttp(servletRequest);

        if ("OPTIONS".equals(httpServletRequest.getMethod())) {

            return true;

        }

        return super.isAccessAllowed(servletRequest, servletResponse, o);

    }

 

}

  5、处理跨域

@Override

   public void addCorsMappings(CorsRegistry registry) {

       registry.addMapping("/**")

               .allowedOrigins("*")

               .allowedMethods("PUT""DELETE""GET""POST")

               .allowedHeaders("*")

               .exposedHeaders("access-control-allow-headers""access-control-allow-methods""access-control-allow" +

                       "-origin""access-control-max-age""X-Frame-Options","Authorization")

               .allowCredentials(false).maxAge(3600);

 

   }


腾讯云推出云产品限时特惠抢购活动:2C2G云服务器7.9元/月起
本文链接:https://www.jhelp.net/p/OtZgT6cKpWKZgvqK (转载请保留)。
关注下面的标签,发现更多相似文章