栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro 整合JWT篇09

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro 整合JWT篇09

功能:

登录成功时返回token,之后的请求携带token进行认证授权;
之前是认证一次,由session存储用户信息,
现在是使用jwt(无状态)每次请求都需要重新认证,所以禁用session

1 github:源码地址 2 shiro09 子工程


    4.0.0

    
        com.yzm
        shiro
        0.0.1-SNAPSHOT
        ../pom.xml 
    

    shiro09
    0.0.1-SNAPSHOT
    jar
    shiro09
    Demo project for Spring Boot

    
        
            com.yzm
            common
            0.0.1-SNAPSHOT
        

        
        
            io.jsonwebtoken
            jjwt
            0.9.1
        

        
        
            org.crazycake
            shiro-redis
            3.2.3
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



项目结构

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:/mapper
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }
4 自定义拦截器JwtFilter

指定哪些url需要携带token并生成JwtToken
isAccessAllowed() 该拦截器的入口
createToken() 获取请求头的token并判断是否过期,未过期生成JwtToken

package com.yzm.shiro09.config;

import com.alibaba.fastjson.JSONObject;
import com.yzm.common.entity.HttpResult;
import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtFilter extends AuthenticatingFilter {

    
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    
    @Override
    protected void postHandle(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
    }

    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean allowed = false;
        try {
            allowed = executeLogin(request, response);
        } catch (IllegalStateException e) {
            log.error("Not found any token");
        } catch (Exception e) {
            log.error("Error occurs when login", e);
        }
        return allowed || super.isPermissive(mappedValue);
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = this.createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        } else {
            try {
                Subject subject = this.getSubject(request, response);
                subject.login(token);
                return this.onLoginSuccess(token, subject, request, response);
            } catch (AuthenticationException var5) {
                return this.onLoginFailure(token, var5, request, response);
            }
        }
    }

    
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String jwtToken = JwtUtils.getTokenFromRequest(WebUtils.toHttp(request));
        if (StringUtils.isNotBlank(jwtToken)) return new JwtToken(jwtToken);
        return null;
    }

    
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        HttpResult result = new HttpResult(500, "请求失败", null);
        response.getWriter().print(JSONObject.toJSONString(result, true));
        response.getWriter().flush();
        response.getWriter().close();
        return false;
    }

    
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        log.info("token认证成功");
        if (token instanceof JwtToken) {
            JwtToken jwtToken = (JwtToken) token;
            String tokenStr = jwtToken.getToken();
            if (JwtUtils.isExpired(tokenStr)) {
                log.info("刷新token");
                String username = (String) subject.getPrincipal();
                Map map = new HashMap<>();
                map.put(JwtUtils.USERNAME, username);
                tokenStr = JwtUtils.generateToken(map);
            }
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            httpResponse.setHeader("token", tokenStr);
        }
        return true;
    }

    
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        log.info("token认证失败");
        if (e instanceof IncorrectCredentialsException) {
            log.error("刷新token");
            JwtToken jwtToken = (JwtToken) token;
            String tokenStr = jwtToken.getToken();
            String username = JwtUtils.getUsernameFromToken(tokenStr);

            Map map = new HashMap<>();
            map.put(JwtUtils.USERNAME, username);
            String newToken = JwtUtils.generateToken(map);
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            httpResponse.setHeader("token", newToken);
        }
        if (e instanceof UnknownAccountException) {
            log.error("账号异常");
        }
        return false;
    }

}

JwtFilter拦截器的处理逻辑跟登录接口是差不多的,生成对应的token并调用subject.login(token)

    @PostMapping("doLogin")
    @ResponseBody
    public Object doLogin(@RequestParam String username, @RequestParam String password, boolean rememberMe) {
        String token;
        try {
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            if (rememberMe) usernamePasswordToken.setRememberMe(true);
            Subject subject = SecurityUtils.getSubject();
            subject.login(usernamePasswordToken);

            // 生成token
            Map map = new HashMap<>();
            map.put(JwtUtils.USERNAME, username);
            token = JwtUtils.generateToken(map);
        } catch (IncorrectCredentialsException ice) {
            return HttpResult.error("password error!");
        } catch (UnknownAccountException uae) {
            return HttpResult.error("username error!");
        }

        return HttpResult.ok(token);
    }

在ShrioConfig#shiroFilter,注入到Shiro并使用

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置无权限时跳转的 url
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        // 注入自定义拦截器
        Map filters = new linkedHashMap<>();
        filters.put("autht", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // url拦截规则
        Map definitionMap = new linkedHashMap<>();
        definitionMap.put("/home", "anon");
        definitionMap.put("/register", "anon");
        definitionMap.put("/login", "anon");
        definitionMap.put("/doLogin", "anon");
        definitionMap.put("/401", "anon");
        definitionMap.put("/logout", "anon");

        // 禁用session创建,还需要noSessionCreation
        definitionMap.put("
public class JwtUtils implements Serializable {

    private static final long serialVersionUID = 8527289053988618229L;
    
    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Basic ";
    
    public static final String USERNAME = Claims.SUBJECT;
    public static final String PASSWORD = "password";
    
    public static final String AUTHORITIES = "authorities";
    
    private static final String SECRET = "abcdefg";
    private static final String JWT_SECRET = "7786df7fc3a34e26a61c034d5ec8245d";
    
    public static final long TOKEN_EXPIRED_TIME = 5 * 60 * 1000L;
    public static final long TOKEN_REFRESH_TIME = 2 * 60 * 1000L;

    public static String generateToken(Map claims) {
        return generateToken(claims, 0L);
    }

    
    public static String generateToken(Map claims, long expireTime) {
        if (expireTime <= 0L) expireTime = TOKEN_EXPIRED_TIME;

        Map headMap = new HashMap<>();
        headMap.put("typ", "JWT");
        headMap.put("alg", "HS256");

        return Jwts.builder()
                .setHeader(headMap)
                //JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击
                .setId(UUID.randomUUID().toString())
                //.setIssuer("该JWT的签发者,是否使用是可选的")
                //.setSubject("该JWT所面向的用户,是否使用是可选的")
                //.setAudience("接收该JWT的一方,是否使用是可选的")
                //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                //签发时间(token生成时间)
                .setIssuedAt(new Date())
                //生效时间(在指定时间之前令牌是无效的)
                .setNotBefore(new Date())
                //过期时间(在指定时间之后令牌是无效的)
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                //设置签名使用的签名算法和签名使用的秘钥
//                .signWith(SignatureAlgorithm.HS256, JWT_SECRET)
                .signWith(SignatureAlgorithm.HS256, generalKey())
                .compact();
    }

    
    public static Claims verifyToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    //签名秘钥
//                    .setSigningKey(JWT_SECRET)
                    .setSigningKey(generalKey())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            // token过期是直接抛出异常的,但仍然可以获取到claims对象
            claims = e.getClaims();
        }
        return claims;
    }

    
    public static SecretKey generalKey() {
        byte[] encodedKey = base64.decodebase64(SECRET);
//        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return new SecretKeySpec(encodedKey, SignatureAlgorithm.HS256.getJcaName());
    }

    
    public static boolean isExpired(String token) {
        Claims claims = verifyToken(token);
        //和当前时间进行对比来判断是否过期
        return claims.getExpiration().before(new Date());
    }

    
    public static String getUsernameFromToken(String token) {
        Claims claims = verifyToken(token);
        return claims.getSubject();
    }

    
    public static String getTokenFromRequest(HttpServletRequest request) {
        String token = request.getHeader(TOKEN_HEADER);
        if (token == null) token = request.getHeader("token");
        if (StringUtils.isBlank(token)) return null;

        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.substring(TOKEN_PREFIX.length());
        }
        return token;
    }
}

6 认证和授权
package com.yzm.shiro09.config;

import com.yzm.shiro09.entity.Permissions;
import com.yzm.shiro09.entity.Role;
import com.yzm.shiro09.entity.User;
import com.yzm.shiro09.service.PermissionsService;
import com.yzm.shiro09.service.RoleService;
import com.yzm.shiro09.service.UserService;
import org.apache.shiro.authc.*;
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 java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


public class MyShiroRealm extends AuthorizingRealm {

    private final UserService userService;
    private final RoleService roleService;
    private final PermissionsService permissionsService;

    public MyShiroRealm(UserService userService, RoleService roleService, PermissionsService permissionsService) {
        this.userService = userService;
        this.roleService = roleService;
        this.permissionsService = permissionsService;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        // 查询用户,获取角色ids
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        List roleIds = Arrays.stream(user.getRIds().split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());

        // 查询角色,获取角色名、权限ids
        List roles = roleService.listByIds(roleIds);
        Set roleNames = new HashSet<>(roles.size());
        Set permIds = new HashSet<>();
        roles.forEach(role -> {
            roleNames.add(role.getRName());
            Set collect = Arrays.stream(
                    role.getPIds().split(",")).map(Integer::parseInt).collect(Collectors.toSet());
            permIds.addAll(collect);
        });

        // 获取权限名称
        List permissions = permissionsService.listByIds(permIds);
        List permNames = permissions.stream().map(Permissions::getPName).collect(Collectors.toList());

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(roleNames);
        authorizationInfo.addStringPermissions(permNames);
        return authorizationInfo;
    }

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户名跟密码
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        // 也可以这样获取
        //String username = (String) authenticationToken.getPrincipal();
        //String password = new String((char[]) authenticationToken.getCredentials());

        // 查询用户是否存在
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            throw new UnknownAccountException();
        }

        return new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                // 用户名 + 盐
                new MySimpleByteSource(user.getUsername() + user.getSalt()),
                getName()
        );
    }
}

JwtRealm.java

package com.yzm.shiro09.config;

import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.entity.User;
import com.yzm.shiro09.service.UserService;
import com.yzm.shiro09.utils.JwtUtils;
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;

public class JwtRealm extends AuthorizingRealm {

    private final UserService userService;

    public JwtRealm(UserService userService) {
        this.userService = userService;
    }

    
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
        JwtToken jwtToken = (JwtToken) authToken;
        String token = jwtToken.getToken();

        String username = JwtUtils.getUsernameFromToken(token);
        if (username == null) {
            throw new AuthenticationException("token过期,请重新登录");
        }

        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            throw new AuthenticationException("账号异常");
        }

        return new SimpleAuthenticationInfo(
                username,
                token,
                getName());
    }

    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return new SimpleAuthorizationInfo();
    }
}

JwtRealm的凭证信息是token,所以我们需要自定义凭证匹配器JwtCredentialsMatcher,主要就是校验下token是否过期

package com.yzm.shiro09.config;

import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.utils.JwtUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;


    public class JwtCredentialsMatcher implements CredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
        if (authenticationToken instanceof JwtToken) {
            String token = (String) authenticationInfo.getCredentials();
            return !JwtUtils.isExpired(token);
        }
        return false;
    }
}

7 ShiroConfig 配置类
package com.yzm.shiro09.config;

import com.yzm.shiro09.service.PermissionsService;
import com.yzm.shiro09.service.RoleService;
import com.yzm.shiro09.service.UserService;
import com.yzm.shiro09.utils.EncryptUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageevaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageevaluator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.cookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.cookie;
import org.apache.shiro.web.servlet.Simplecookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.Filter;
import java.util.Arrays;
import java.util.linkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

    private final UserService userService;
    private final RoleService roleService;
    private final PermissionsService permissionsService;

    public ShiroConfig(UserService userService, RoleService roleService, PermissionsService permissionsService) {
        this.userService = userService;
        this.roleService = roleService;
        this.permissionsService = permissionsService;
    }

    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(EncryptUtils.ALGORITHM_NAME);
        hashedCredentialsMatcher.setHashIterations(EncryptUtils.HASH_ITERATIONS);
        //hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    
    @Bean
    public MyShiroRealm shiroRealm() {
        MyShiroRealm shiroRealm = new MyShiroRealm(userService, roleService, permissionsService);
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

        // 开启缓存
        shiroRealm.setCachingEnabled(true);
        //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
        shiroRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
        shiroRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        shiroRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        shiroRealm.setAuthorizationCacheName("authorizationCache");
        return shiroRealm;
    }

    
    @Bean
    public JwtRealm jwtRealm() {
        JwtRealm jwtRealm = new JwtRealm(userService);
        jwtRealm.setCredentialsMatcher(new JwtCredentialsMatcher());
        return jwtRealm;
    }

    
    @Bean
    public cookie simplecookie() {
        Simplecookie cookie = new Simplecookie("rememberMe");
        //设为true后,只能通过http访问,javascript无法访问
        //防止xss读取cookie
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        //存活时间,单位秒;-1表示关闭浏览器该cookie失效
        cookie.setMaxAge(-1);
        return cookie;
    }

    @Bean
    public cookieRememberMeManager rememberMeManager() {
        cookieRememberMeManager rememberMeManager = new cookieRememberMeManager();
        rememberMeManager.setcookie(simplecookie());
        //cookie加密的密钥
        //rememberMeManager.setCipherKey(base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }

    
    @Bean
    protected SessionStorageevaluator sessionStorageevaluator() {
        DefaultSessionStorageevaluator sessionStorageevaluator = new DefaultSessionStorageevaluator();
        sessionStorageevaluator.setSessionStorageEnabled(false);
        return sessionStorageevaluator;
    }

    
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("127.0.0.1:6379");
        redisManager.setPassword("1234");
        redisManager.setDatabase(0);
        return redisManager;
    }

    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        // redis中针对不同用户缓存
        redisCacheManager.setPrincipalIdFieldName("username");
        // 用户权限信息缓存时间
        redisCacheManager.setExpire(300);
        return redisCacheManager;
    }

    
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置单个realm
        //securityManager.setRealm(shiroRealm());

        // 验证器
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);

        // 自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer
        securityManager.setRealms(Arrays.asList(shiroRealm(), jwtRealm()));

        // 记住我
        securityManager.setRememberMeManager(rememberMeManager());

        // 缓存
        securityManager.setCacheManager(redisCacheManager());

        // 关闭shiro自带的session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        subjectDAO.setSessionStorageevaluator(sessionStorageevaluator());
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置无权限时跳转的 url
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        // 注入自定义拦截器
        Map filters = new linkedHashMap<>();
        filters.put("autht", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // url拦截规则
        Map definitionMap = new linkedHashMap<>();
        definitionMap.put("/home", "anon");
        definitionMap.put("/register", "anon");
        definitionMap.put("/login", "anon");
        definitionMap.put("/doLogin", "anon");
        definitionMap.put("/401", "anon");
        definitionMap.put("/logout", "anon");

        // 禁用session创建,还需要noSessionCreation
        definitionMap.put("
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        // 登录后没有权限跳转到/401
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/401");
        // 未登录访问接口跳转到/login
        properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/login");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }
}

测试

登录yzm

访问/user/select

在这里我们设置了不创建Session

@Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ...

        // 注入自定义拦截器
        Map filters = new linkedHashMap<>();
        filters.put("autht", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // url拦截规则
        ...

        // 禁用session创建,还需要noSessionCreation
        definitionMap.put("
@Slf4j
public class MultiRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doMultiRealmAuthentication(Collection realms, AuthenticationToken token)
            throws AuthenticationException {
        AuthenticationStrategy strategy = getAuthenticationStrategy();
        AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
        if (log.isTraceEnabled()) {
            log.trace("Iterating through {} realms for PAM authentication", realms.size());
        }

        AuthenticationException authenticationException = null;
        for (Realm realm : realms) {
            aggregate = strategy.beforeAttempt(realm, token, aggregate);

            if (realm.supports(token)) {
                log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);

                AuthenticationInfo info = null;
                try {
                    info = realm.getAuthenticationInfo(token);
                } catch (AuthenticationException e) {
                    authenticationException = e;
                    if (log.isDebugEnabled()) {
                        String msg = "Realm [" + realm
                                + "] threw an exception during a multi-realm authentication attempt:";
                        log.debug(msg, e);
                    }
                }

                aggregate = strategy.afterAttempt(realm, token, info, aggregate, authenticationException);
            } else {
                log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);
            }
        }
        if (authenticationException != null) {
            throw authenticationException;
        }

        aggregate = strategy.afterAllAttempts(token, aggregate);
        return aggregate;
    }
}

在ShiroConfig#securityManager

    
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置单个realm
        //securityManager.setRealm(shiroRealm());

        // 自定义验证器
        ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);

       ...
        return securityManager;
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/336905.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号