当前栏目:java世界

shiro与token无状态单点登录

2023-02-12269

shiro与token无状态单点登录
1.session 的运作通过一个session_id来进行。session_id通常是存放在客户端的 cookie 中,众所周知,当客户端请求成功,服务端写入session数据,向客户端浏览器返回sessionid,浏览器将sessionid保存在cookie中,当用户再次访问服务器时,会携带sessionid,服务器会拿着sessionid从服务器获取session数据,然后进行用户信息查询,查询到,就会将查询到的用户信息返回,从而实现状态保持。session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

2.token :json web token ,由头部,载荷,签名三部分组成,无状态的,可以在多个服务间共享,安全性较高

步入正题:

1.shiroConfig

@Configuration
@Slf4j
public class ShiorConfig2 {

    /**
     * 注入自定义的 Realm
     * @return MyRealm
     */
    @Bean
    public MyTokenRealm myAuthRealm() {
        MyTokenRealm myRealm = new MyTokenRealm();
        log.info("=============myRealm2===========================");
        return myRealm;
    }

    @Bean
    public SecurityManager securityManager(MyTokenRealm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        //关闭shiro自带的session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

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

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
       // 定义 shiroFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();

        // 设置自定义的 securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置默认登录的 URL,身份认证失败会访问该 URL
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置成功之后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/success");
        // 设置未授权界面,权限认证失败会访问该 URL
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");

        // LinkedHashMap 是有序的,进行顺序拦截器配置
        Map<String,String> filterChainMap = new LinkedHashMap<>();

        // 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
        filterChainMap.put("/css/**", "anon");
        filterChainMap.put("/imgs/**", "anon");
        filterChainMap.put("/js/**", "anon");
        filterChainMap.put("/swagger-*/**", "anon");
        filterChainMap.put("/swagger-ui.html/**", "anon");
        // 登录 URL 放行
        filterChainMap.put("/login", "anon");

        // 以“/user/admin” 开头的用户需要身份认证,authc 表示要进行身份认证
        filterChainMap.put("/user/admin*", "roles[admin]");
        // “/user/student” 开头的用户需要角色认证,是“admin”才允许
        filterChainMap.put("/user/student*/**", "roles[admin]");
        // “/user/teacher” 开头的用户需要权限认证,是“user:create”才允许
        filterChainMap.put("/user/teacher*/**", "perms[\"user:create\"]");

        // 配置 logout 过滤器
        filterChainMap.put("/logout", "logout");

        // 设置 shiroFilterFactoryBean 的 FilterChainDefinitionMap
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        logger.info("====shiroFilterFactoryBean注册完成====");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
2: tokenUtil 工具

/**
 * 设置过期时间 1分钟
 */
private static final long EXPIRE_TIME=60 * 1000;
private static final String TOKEN_SECRET="abcd";
/**
 * 产生token
 * @param useName
 * @param userId
 * @return
 */
public static String Sign(String useName , long userId){
    try{
        //这里将useName 和 userId 存入了Token,在下面的解析中,也会有解析的方法可以获取到Token里面的数据
        //Token过期的时间
        Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
        System.out.println("date"+date);
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);

        //设置头部信息
        Map<String,Object> header = new HashMap<>();
        header.put("typ","JWT");
        header.put("alg","HS256");

        //附带username和userid信息,存储到token中,生成签名
        return JWT.create()
                .withHeader(header)

                //存储自己想要留存给客户端浏览器的内容
                .withClaim("userName",useName)
                .withClaim("userId",userId)
                .withExpiresAt(date)
                .sign(algorithm);
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

/**
 * token校验是否正确
 * @param token
 * @return
 */

public static boolean verify(String token){
    try{
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);

        JWTVerifier verifier = JWT.require(algorithm).build();

        DecodedJWT decodedJWT = verifier.verify(token);

        return true;

    }catch (Exception e){
        System.out.println("Token超时,需要重新登录");
    }

    return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
3.自定义Realm

/**
 * Author:XuDing
 * Date:2020/4/26
 */

public class MyTokenRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    //doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息。
    //doGetAuthorizationInfo() 方法:为当前登录成功的用户授予权限和分配角色。
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String userName = JWTUtil.getUsername(principalCollection.toString());
        // 获取用户名
        SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
        // 给该用户设置角色,角色信息存在 t_role 表中取
        Set<String> roles = userService.getRoles(userName);
        System.err.println("roles========>"+roles);
        authorizationInfo.setRoles(roles);
        // 给该用户设置权限,权限信息存在 t_permission 表中取
        authorizationInfo.setStringPermissions(userService.getPermissions(userName));
        return authorizationInfo;

    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据 Token 获取用户名
        //  UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authcToken;
        String userName = (String) authenticationToken.getPrincipal();
        // 根据用户名从数据库中查询该用户
        User user = userService.getByUsername(userName);
        if(user !=null){
            // 把当前用户存到 Session 中
            SecurityUtils.getSubject().getSession().setAttribute("user",user);
            // 传入用户名和密码进行身份认证,并返回认证信息
//            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "myRealm");
//            return authcInfo;
            return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        }else {
            return null;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
登录接口

    /**
     * 用户登录接口
     * @param user user
     * @param request request
     * @return string
     */
    @GetMapping("/login")
    public ResponseResult login(User user, HttpServletRequest request) {

        // 根据用户名和密码创建 Token
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
        System.err.println("token=========>"+token);
        // 获取 subject 认证主体
        Subject subject = SecurityUtils.getSubject();
        try{
            PrincipalCollection principals = subject.getPrincipals();
            System.out.println(principals);
            // 开始认证,这一步会跳到我们自定义的 Realm 中
            subject.login(token);
//            String token2 = JWTUtil.createToken(user.getUserName());
            String token2 = TokenSign.Sign(user.getUserName(), 5);
//            request.getSession().setAttribute("user", user);
            return ResponseResult.success(token2);
        }catch(Exception e){
            e.printStackTrace();
            request.getSession().setAttribute("user", user);
            request.setAttribute("error", "用户名或密码错误!");
            return ResponseResult.error("失败");
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
拦截器

@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    //首先从请求头中获取jwt串,与页面约定好存放jwt值的请求头属性名为User-Token
    String jwt = httpServletRequest.getHeader("User-Token");
    log.info("[登录校验拦截器]-从header中获取的jwt为:{}", jwt);
    //判断jwt是否有效
    if(StringUtils.isNotBlank(jwt)){
            boolean verify1 = TokenSign.verify(jwt);
            if(verify1){
                return true;
            }
    }
    return false;
}

@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
退出:
————————————————
版权声明:本文为CSDN博主「南方的烈阳.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45528177/article/details/108563573

相关阅读