un1queyan

  • 主页
  • 随笔
  • 归档
所有文章 友链 关于我

un1queyan

  • 主页
  • 随笔
  • 归档

Shiro安全框架


阅读数: 次    2021-09-10

Shiro安全框架

一、Shiro概述

1、Apache Shiro

shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码加密和会话管理等。

常见的安全框架:

apache shiro

spring security

sa-token 前后端分离、分布式集群

2、主要作用

(1)身份认证

(2)授权

(3)加密

(4)会话管理

(5)缓存

3、核心组件

img

(1)Subject(用户)

即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。

(2)SecurityManager(安全管理器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

具体服务包括:

###### Authentication:身份认证/登录(账号密码验证)。

###### Authorization:授权,即角色或者权限验证。

###### Session Manager:会话管理,用户登录后的session相关管理。

###### Cryptography:加密,密码加密等。

###### Web Support:Web支持,集成Web环境。

###### Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。

###### Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。

###### Testing:测试支持;

###### Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。

###### Remember Me:记住我,登录后,下次再来的话不用登录了。

(3)Realm(数据源)

Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

image-20211129100547246

二、springboot整合shiro

image-20211129103548403

1、导入依赖

shiro-spring

1
2
3
4
5
6
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>

2、配置shiro

过滤器 , 安全管理器 ,数据源

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
package com.example.springbootshirodemo.config;

import com.example.springbootshirodemo.shiro.MyRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

/**
* 核心过滤器ShiroFilter
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}


/**
* security manager
* DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(MyRealm realm){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//设置realm,支持多个reaml
securityManager.setRealm(realm);
return securityManager;
}

/**
* realm
*/
@Bean
public MyRealm getRealm(){
MyRealm realm=new MyRealm();
return realm;
}


}

3、编写Realm

1
AuthorizingRealm  extends    AuthenticatingRealm  extends  CachingRealm   implements  Realm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyRealm    extends  AuthorizingRealm {

@Override
/**
* 获取授权信息
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}

@Override
/**
* 获取认证信息
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}

三、身份认证和退出

1、认证流程

image-20211129105904336

2、身份认证操作

(1)数据库设计和配置mybatis

五张表

user,role,permission,user_role,role_permission

1
2
3
4
5
6
7
8
9
#mybatis
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql:///shiro?serverTimezone=Asia/Shanghai

mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.springbootshirodemo.domain
mybatis.configuration.map-underscore-to-camel-case=true

(2)编写domain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.springbootshirodemo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int uid;
private String username;
private String passwd;
private Date regTime;
}

(3)mapper

1
2
3
4
5
6
7
8
9
10
package com.example.springbootshirodemo;

import com.example.springbootshirodemo.domain.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {

User findUserByUsername(String username);
}

(4)映射文件

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.springbootshirodemo.UserMapper">

<select id="findUserByUsername" parameterType="string" resultType="User">
select * from user where username=#{username}
</select>

</mapper>

(5)编写realm

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
package com.example.springbootshirodemo.shiro;

import com.example.springbootshirodemo.UserMapper;
import com.example.springbootshirodemo.domain.User;
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.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyRealm extends AuthorizingRealm {

@Autowired
private UserMapper userMapper;

public String getName(){
return "myrealm";
}

@Override
/**
* 获取授权信息
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}

@Override
/**
* 获取认证信息
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取当前用户的用户名
String username = (String)authenticationToken.getPrincipal();

//去数据库中查询该用户
User user=userMapper.findUserByUsername(username);

if(user==null){
return null;
}

AuthenticationInfo info=new SimpleAuthenticationInfo(username,user.getPasswd(),getName());
return info;
}
}

(6)修改shiroconfig

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
package com.example.springbootshirodemo.config;

import com.example.springbootshirodemo.shiro.MyRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

/**
* 核心过滤器ShiroFilter
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}


/**
* security manager
* DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(MyRealm realm){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//设置realm,支持多个reaml
securityManager.setRealm(realm);
return securityManager;
}

// /**
// * realm
// */
// @Bean
// public MyRealm getRealm(){
// MyRealm realm=new MyRealm();
// return realm;
// }


}

(7)编写登录

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

@RestController
public class UserController {


@RequestMapping("/user/login")
public String login(String username,String passwd){
//获取当前用户
Subject currentUser= SecurityUtils.getSubject();
//封装凭证
UsernamePasswordToken token=new UsernamePasswordToken(username,passwd);
//身份认证
//认证失败:抛异常
try {
currentUser.login(token);
}catch (UnknownAccountException e){
return "error:用户名不存在";
}catch (IncorrectCredentialsException e){
return "error:密码错误";
}catch (Exception e){
return "error: 未知异常";
}

//响应
return "login ok";
}

}

3、退出认证

1
2
3
4
5
6
7
@RequestMapping("/ty/logout")
public String logout()
{
//清除认证信息,退出
SecurityUtils.getSubject().logout();
return "index";
}

4、记住我

认证通过,将认证信息保存下来。

下次可以直接访问,无需再认证。

(1)页面

1
2
3
4
5
6
7
8
9
登录页面:
<form method="post" action="/user/login">
用户名:<input type="text" name="username">
密码:<input type="password" name="passwd">
7天免登录
<input type="checkbox" name="rememberMe">

<input type="submit" value="登录">
</form>

(2)controller

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
@RequestMapping("/user/login")
public String login(String username,String passwd,boolean rememberMe){
System.out.println(rememberMe);
//获取当前用户
Subject currentUser= SecurityUtils.getSubject();
//封装凭证
//记住我rememberMe:必须在认证前设置有效
UsernamePasswordToken token=new UsernamePasswordToken(username,passwd,rememberMe);

//身份认证
//认证失败:抛异常
try {
/*
执行login:
securitymanager调用认证器;
认证器通过realm获取用户信息;
认证器将用户输入的密码,采用指定的加密器(md5,2,salt)进行加密;
使用密文和数据库的密码进行比对。
*/
currentUser.login(token);
}catch (UnknownAccountException e){
return "error:用户名不存在";
}catch (IncorrectCredentialsException e){
return "error:密码错误";
}catch (Exception e){
return "error: 未知异常";
}


return "index";
}

(3)设置记住我

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 记住我
*/
@Bean
public SimpleCookie getSimpleCookie(){
SimpleCookie simpleCookie= new SimpleCookie();
simpleCookie.setHttpOnly(true);
//7天
simpleCookie.setName("rememberMe");
simpleCookie.setMaxAge(604800);

return simpleCookie;
}

@Bean
public CookieRememberMeManager getCookieRememberMeManager(SimpleCookie simpleCookie){
CookieRememberMeManager rememberMeManager=new CookieRememberMeManager();
rememberMeManager.setCookie(simpleCookie);
//设置统一密钥,防止重启密钥发生变化导致记住我验证不通过
rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return rememberMeManager;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* security manager
* DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(MyRealm realm,CookieRememberMeManager rememberMeManager){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//设置realm,支持多个reaml
securityManager.setRealm(realm);
//设置记住我
securityManager.setRememberMeManager(rememberMeManager);
return securityManager;
}

(4)错误Unable to execute ‘doFinal’ with cipher instance

解决:

//设置统一密钥,防止重启密钥发生变化导致记住我验证不通过
rememberMeManager.setCipherKey(Base64.decode(“4AvVhmFLUs0KTA3Kprsdag==”));

四、授权

一个用户拥有什么权限,就能进行什么操作。反之,不能操作。

权限设计:RBAC

Role-Base Access-Controll 基于角色的权限设计

Resource-Base Access-Controll 基于资源的权限设计

需要5张表:

user 、 role、 user_role

permission、role_permission

查找当前用户的角色和权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.springbootshirodemo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int uid;
private String username;
private String passwd;
private Date regTime;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.springbootshirodemo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private int rid;
private String rname;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.springbootshirodemo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
private int pid;
private String pname;
private String purl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.springbootshirodemo.mapper;

import com.example.springbootshirodemo.domain.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserMapper {

User findUserByUsername(String username);

List<String> findRoleByUsername(String username);

List<String> findPermissionByUsername(String username);
}
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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.springbootshirodemo.mapper.UserMapper">

<select id="findUserByUsername" parameterType="string" resultType="User">
select * from user where username=#{username}
</select>

<select id="findRoleByUsername" parameterType="string" resultType="string">
SELECT r.rname
FROM user u,role r,user_role ur
where u.uid=ur.uid
and r.rid=ur.rid
and u.username=#{username}
</select>

<select id="findPermissionByUsername" parameterType="string" resultType="string">
SELECT p.pname
FROM user u,role r,user_role ur,permission p,role_permission rp
where u.uid=ur.uid
and r.rid=ur.rid
and r.rid=rp.rid
and p.pid=rp.pid
and u.username=#{username}
</select>

</mapper>
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
package com.example.springbootshirodemo.shiro;

import com.example.springbootshirodemo.domain.Role;
import com.example.springbootshirodemo.mapper.UserMapper;
import com.example.springbootshirodemo.domain.User;
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 java.util.List;

@Component
public class MyRealm extends AuthorizingRealm {

@Autowired
private UserMapper userMapper;

public String getName(){
return "myrealm";
}

@Override
/**
* 获取授权信息
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前用户的用户名
String username = (String)principalCollection.getPrimaryPrincipal();

//根据用户名查询该用户的所有角色和权限
List<String> roleList=userMapper.findRoleByUsername(username);
List<String> permissionList=userMapper.findPermissionByUsername(username);

//返回当前用户的所有角色和权限
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addRoles(roleList);
info.addStringPermissions(permissionList);

return info;
}

@Override
/**
* 获取认证信息
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取当前用户的用户名
String username = (String)authenticationToken.getPrincipal();

//去数据库中查询该用户
User user=userMapper.findUserByUsername(username);

if(user==null){
return null;
}

AuthenticationInfo info=new SimpleAuthenticationInfo(username,user.getPasswd(),getName());
return info;
}
}

image-20211129155545828

1、过滤器授权(基于url拦截的权限管理)

img

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
package com.example.springbootshirodemo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {


@RequestMapping("/user/login")
public String login(String username,String passwd){
//获取当前用户
Subject currentUser= SecurityUtils.getSubject();
//封装凭证
UsernamePasswordToken token=new UsernamePasswordToken(username,passwd);
//身份认证
//认证失败:抛异常
try {
currentUser.login(token);
}catch (UnknownAccountException e){
return "error:用户名不存在";
}catch (IncorrectCredentialsException e){
return "error:密码错误";
}catch (Exception e){
return "error: 未知异常";
}

//响应
return "login ok";
}

@RequestMapping("/student/add")
public String addStudent(){
return "add student ok";
}

@RequestMapping("/student/delete")
public String deleteStudent(){
return "delete student ok";
}


@RequestMapping("/student/update")
public String updateStudent(){
return "update student ok";
}

@RequestMapping("/student/select")
public String selectStudent(){
return "select student ok";
}

}
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
package com.example.springbootshirodemo.config;

import com.example.springbootshirodemo.shiro.MyRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

/**
* 核心过滤器ShiroFilter
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//设置权限
Map<String, String> filterMap=new HashMap<>();
filterMap.put("/student/add","authc"); //认证通过
filterMap.put("/student/select","anon"); //所有人
// filterMap.put("/student/update","perms[student:*]");
filterMap.put("/student/update","perms[student:update]");
filterMap.put("/student/delete","roles[admin]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

// 页面设置
shiroFilterFactoryBean.setLoginUrl("/login.html");


return shiroFilterFactoryBean;
}


/**
* security manager
* DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(MyRealm realm){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//设置realm,支持多个reaml
securityManager.setRealm(realm);
return securityManager;
}

// /**
// * realm
// */
// @Bean
// public MyRealm getRealm(){
// MyRealm realm=new MyRealm();
// return realm;
// }


}

2、注解授权

image-20211130100659950

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RequiresAuthentication:
使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证

RequiresGuest:
使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。

RequiresPermissions:
当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。



RequiresRoles:
  当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。

  RequiresUser
当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。

开启注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 开启权限注解(aop)
* 1、通知
* 2、自动代理
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator autoProxyCreator=new DefaultAdvisorAutoProxyCreator();
return autoProxyCreator;
}

注解使用:

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
package com.example.springbootshirodemo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {


@RequestMapping("/user/login")
public String login(String username,String passwd){
//获取当前用户
Subject currentUser= SecurityUtils.getSubject();
//封装凭证
UsernamePasswordToken token=new UsernamePasswordToken(username,passwd);
//身份认证
//认证失败:抛异常
try {
currentUser.login(token);
}catch (UnknownAccountException e){
return "error:用户名不存在";
}catch (IncorrectCredentialsException e){
return "error:密码错误";
}catch (Exception e){
return "error: 未知异常";
}

//响应
// 记住我
token.setRememberMe(true);
return "login ok";
}

@RequestMapping("/student/add")
//必须认证通过
// @RequiresAuthentication
// 认证通过或者记住我
@RequiresUser
public String addStudent(){
return "add student ok";
}

@RequestMapping("/student/delete")
//必须访客访问,认证之后不能访问
@RequiresGuest
public String deleteStudent(){
return "delete student ok";
}

@RequestMapping("/student/update")
//必须由指定的权限
@RequiresPermissions("student:update")
public String updateStudent(){
return "update student ok";
}

@RequestMapping("/student/select")
//必须由指定的角色
@RequiresRoles("admin")
public String selectStudent(){
return "select student ok";
}

}

五、thymeleaf集成shiro标签

1、作用

通过shiro权限来控制页面上的内容是否显示。

image-20211130112045411

image-20211130112121788

2、导入依赖

1
2
3
4
5
6
<!--shiro集成thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
1
2
3
4
5
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3、配置

1
2
3
4
5
6
7
/**
* thymeleaf集成shiro
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}

4、页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
登录页面:
<form method="post" action="/user/login">
用户名:<input type="text" name="username">
密码:<input type="password" name="passwd">

<input type="submit" value="登录">
</form>
</body>
</html>
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
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>人力资源管理系统</h1>
<shiro:authenticated>
<h3>你好,XXX</h3>
</shiro:authenticated>
<shiro:notauthenticated>
<h3>游客,<a th:href="@{/ty/login}">请登录</a></h3>
</shiro:notauthenticated>

<shiro:hasRole name="admin">
<div>
<a> 添加学生</a>
</div>
</shiro:hasRole>
<shiro:hasPermission name="student:delete">
<div>
<a> 删除学生</a>
</div>
</shiro:hasPermission>
<div>
<a> 修改学生</a>
</div>
<div>
<a> 查询学生</a>
</div>

</body>
</html>

5、controller

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
package com.example.springbootshirodemo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ThymeleafController {

@RequestMapping("/ty/index")
public String index(){
System.out.println("index");
// /index.html
return "index";
}

@RequestMapping("/ty/login")
public String loginpage(){
return "login";
}


@RequestMapping("/user/login")
public String login(String username,String passwd){
//获取当前用户
Subject currentUser= SecurityUtils.getSubject();
//封装凭证
UsernamePasswordToken token=new UsernamePasswordToken(username,passwd);
//身份认证
//认证失败:抛异常
try {
currentUser.login(token);
}catch (UnknownAccountException e){
return "error:用户名不存在";
}catch (IncorrectCredentialsException e){
return "error:密码错误";
}catch (Exception e){
return "error: 未知异常";
}

//响应
// 记住我
token.setRememberMe(true);
return "index";
}

}

6、测试

六、加密

1、config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* realm
*/
@Bean
public MyRealm getRealm(HashedCredentialsMatcher credentialsMatcher){
MyRealm realm=new MyRealm();
realm.setCredentialsMatcher(credentialsMatcher);
return realm;
}

/**
* 加密
*/
public HashedCredentialsMatcher getHashedCredentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//加密算法
credentialsMatcher.setHashAlgorithmName("md5");
//散列次数
credentialsMatcher.setHashIterations(2);
return credentialsMatcher;
}

2、realm

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
package com.example.springbootshirodemo.shiro;

import com.example.springbootshirodemo.domain.Role;
import com.example.springbootshirodemo.mapper.UserMapper;
import com.example.springbootshirodemo.domain.User;
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.authc.credential.CredentialsMatcher;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

//@Component
public class MyRealm extends AuthorizingRealm {

@Autowired
private UserMapper userMapper;

public String getName(){
return "myrealm";
}

@Override
/**
* 获取授权信息
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前用户的用户名
String username = (String)principalCollection.getPrimaryPrincipal();

//根据用户名查询该用户的所有角色和权限
List<String> roleList=userMapper.findRoleByUsername(username);
List<String> permissionList=userMapper.findPermissionByUsername(username);

//返回当前用户的所有角色和权限
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addRoles(roleList);
info.addStringPermissions(permissionList);

return info;
}

@Override
/**
* 获取认证信息
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取当前用户的用户名
String username = (String)authenticationToken.getPrincipal();

//去数据库中查询该用户
User user=userMapper.findUserByUsername(username);

if(user==null){
return null;
}
String salt="abcdefg";
AuthenticationInfo info=new SimpleAuthenticationInfo(username,user.getPasswd(), ByteSource.Util.bytes(salt),getName());
return info;
}
}

3、数据库密码加密

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.springbootshirodemo.shiro;

import org.apache.shiro.crypto.hash.SimpleHash;

public class Test {
public static void main(String[] args) {

String passwd="liu";
String salt="abcdefg";
SimpleHash simpleHash=new SimpleHash("md5",passwd,salt,2);
System.out.println(simpleHash.toString());
}
}

4、登录测试

七、缓存管理

1、作用

realm可以使用缓存,提高查询性能。

默认授权有缓存,认证没有缓存。

缓存类型:

shiro内置本地缓存MemoryConstrainedCacheManager

整合第三方缓存:EhCache等

2、操作

1
2
3
4
5
6
7
/**
* 缓存
*/
@Bean
public MemoryConstrainedCacheManager getCacheManager(){
return new MemoryConstrainedCacheManager();
}

可以添加给某个realm,也可以添加给securitymanager

如果添加给securitymanager,所有realm都采用缓存。

1
2
3
4
5
6
7
8
9
10
11
12
    /**
* realm
*/
@Bean
public MyRealm getRealm(HashedCredentialsMatcher credentialsMatcher,MemoryConstrainedCacheManager cacheManager){
MyRealm realm=new MyRealm();
realm.setCredentialsMatcher(credentialsMatcher);

// 设置缓存
realm.setCacheManager(cacheManager);
return realm;
}

八、会话管理

1、shiro支持三种会话管理方式

shiro提供了三个SessionManager的实现

  • DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于非web环境。
  • ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话。
  • DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager自己维护着会话,容器无关。

推荐DefaultWebSessionManager

会话跟踪:shiro中有session对象,客户端浏览器有cookie(JSESSIONID),共同维护一个会话,存储相同的会话编号。

2、session会话管理

(1)session API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取session,默认true
Subject.getSession()
如果当前没有创建session对象则创建。
Subject.getSession(true)
如果当前没有创建session对象则返回null。
Subject.getSession(false)

//获取session id
session.getId()
//获取当前会话的主机地址。
session.getHost()
//设置/获取当前Session的过期时间。
session.getTimeout() & session.setTimeout(毫秒)
//Subject.logout()会自动调用session.stop(),来销毁shiro的会话
session.stop()

(2)会话监听器(SessionListener接口)

1
2
3
4
5
6
①onStart(Session)
监听会话创建事件
②onStop(Session)
监听会话销毁事件
③onExpiration(Session)
监听会话过期事件

(3)sessionDao

session对象默认保存在jvm内存中。

也可以保存在redis等数据库中,使用sessionDao完成。

img

3、配置

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
/**
* 会话管理:DefaultWebSessionManager
*/
@Bean
public DefaultWebSessionManager getDefaultWebSessionManager(){
DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
return sessionManager;
}


/**
* security manager
* DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(MyRealm realm,CookieRememberMeManager rememberMeManager,DefaultWebSessionManager sessionManager){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//设置realm,支持多个reaml
securityManager.setRealm(realm);
//设置记住我
securityManager.setRememberMeManager(rememberMeManager);
//设置会话管理方式
securityManager.setSessionManager(sessionManager);


return securityManager;
}

九、分布式集群

1、分布式集群会话共享

image-20211130193130905

2、操作

(1)搭建redis服务器

image-20211130193705273

(2)springboot整合redis

依赖

1
2
3
4
5
<!-- 操作Redis 只需引入spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
#redis数据源
spring.redis.database=0
spring.redis.host=47.97.254.33
spring.redis.port=6379
#spring.redis.password=123456

#redis pool
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.time-between-eviction-runs=5000

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.springbootshirodemo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.xml.ws.Action;

@SpringBootTest
class SpringBootShiroDemoApplicationTests {
@Autowired
private StringRedisTemplate redisTemplate;

@Test
void contextLoads() {
redisTemplate.opsForValue().set("aaa","111");
String aaa = redisTemplate.opsForValue().get("aaa");
System.out.println(aaa);
}

}

(3)编写RedisSessionDao

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
package com.example.springbootshirodemo.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedisSessionDao extends AbstractSessionDAO {
@Autowired
private RedisTemplate redisTemplate;

private long expire=3600;

@SneakyThrows
@Override
protected Serializable doCreate(Session session) {
//生成id(使用uuid)
Serializable sessionId = this.generateSessionId(session);
//为会话指定唯一的id
this.assignSessionId(session,sessionId);
//将会话保存到redis
redisTemplate.opsForValue().set(sessionId,session,expire, TimeUnit.SECONDS);
return sessionId;
}

@SneakyThrows
@Override
protected Session doReadSession(Serializable serializable) {
if(serializable==null){
return null;
}
Session session = (Session) redisTemplate.opsForValue().get(serializable);
return session;
}

@Override
public void update(Session session) throws UnknownSessionException {
if(session==null||session.getId()==null){
return;
}
session.setTimeout(expire*1000);
redisTemplate.opsForValue().set(session.getId(),session,expire, TimeUnit.SECONDS);
}

@Override
public void delete(Session session) {
if(session==null||session.getId()==null){
return;
}
redisTemplate.delete(session.getId());
}

@Override
public Collection<Session> getActiveSessions() {
return redisTemplate.keys("*");
}
}

(4)shiro配置

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
/**
* sessionDao
*/
@Bean
public RedisSessionDao getRedisSessionDao(){
return new RedisSessionDao();
}

@Primary
@Bean
public RedisTemplate<String,Object> getRedisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate=new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}

/**
* 会话管理:DefaultWebSessionManager
*/
@Bean
public DefaultWebSessionManager getDefaultWebSessionManager(RedisSessionDao sessionDao){
DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();

sessionManager.setSessionDAO(sessionDao);

return sessionManager;
}

(5)测试

十、前后端分离

1、shiro会话跟踪

服务器有session对象

客户端有cookie

但是在前后端分离开发中,一般不使用cookie。

2、解决方案:

token字符串存储在sessionStorage或者localStorage。

请求时,将token放在请求头中headers。

响应时,将token放在响应头中headers。

3、请求处理

(1)重写

自定义一个会话管理器,重写DefaultWebSessionManager的getSessionId(ServletRequest request, ServletResponse response) 。

该方法作用:从请求对象中获取会话编号。

默认从cookie中获取,改为从请求头中获取指定的键值对。

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
package com.example.springbootshirodemo.shiro;

import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class MySessionManager extends DefaultWebSessionManager {

/**
* 从请求中获取sessionId
* @param request
* @param response
* @return
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader("token");

if (id==null||id.trim().equals("")) {
//按默认规则从cookie取sessionId
return super.getSessionId(request, response);
} else {
//如果请求头中有 Authorization 则其值为sessionId
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "url");
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
}
}

}

(2)修改配置

1
2
3
4
5
6
7
8
9
10
11
/**
* 会话管理:DefaultWebSessionManager
*/
@Bean
public MySessionManager getDefaultWebSessionManager(RedisSessionDao sessionDao){
MySessionManager sessionManager=new MySessionManager();

sessionManager.setSessionDAO(sessionDao);

return sessionManager;
}

4、响应处理

默认响应,将会话编号保存在cookie中。

改为:将会话编号保存在响应头的token中。

MySessionManager重写onStart方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onStart(Session session, SessionContext context) {
super.onStart(session, context);
if (!WebUtils.isHttp(context)) {
log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response pair. No session ID cookie will be set.");
} else {
HttpServletRequest request = WebUtils.getHttpRequest(context);
HttpServletResponse response = WebUtils.getHttpResponse(context);

//将会话编号保存在resp的header的token中
Serializable id = session.getId();
response.setHeader("token",id.toString());

request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
}

}

5、测试

十一、JWT

1、JWT

Json Web Token

2、会话跟踪

(1)session和cookie

image-20211201150048206

(2)token

image-20211201150449388

(3)jwt

image-20211201150627644

3、JWT token字符串格式

JWTString

1
Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

header(标头).payload(载荷).Signature(签名)

img

(1)header(标头)

{

​ “alg”:”HS256”,

​ “typ”:”JWT”

}

alg:加密算法名称

type:令牌类型

到目前为止,jwt的签名算法有三种:

  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512

  • RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)

  • ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

(2)payload(载荷)

JWT指定七个默认字段供选择:

image-20211201151841809

还可以自定义字段:

​ username:zhangsan

{

​ “iss”:”hs”,

​ “sub”:”java”,

​ “iat”:”2021-12-01”,

​ “exp”:”2021-12-08”,

​ “jti”:”ajkdhfkaldfkahkjfhjka”,

​ “login”:true,

​ “username”:”zhangsan”

}

(3)Signature(签名)

通过指定的算法和密钥生成的。

secret:服务器端设计的密钥。

使用指定的算法和密钥,将header和payload生成一个加密字符串。

这个字符串就是我们的Signature签名。

HMACSHA256(base64UrlEncode(header)+”.”+base64UrlEncode(payload),secret)

4、JWT插件

官网推荐了6个Java使用JWT的开源库,其中比较推荐使用的是java-jwt和jjwt-root。

img

5、生成token(签名)

(1)引入依赖

1
2
3
4
5
6
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>

(2)生成token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 生成token
*/
@Test
public void test1(){
String token = JWT.create()
//设置header
.withHeader(new HashMap<>())
//设置payload
.withClaim("login", true)
.withClaim("username", "zhangsan")
.withExpiresAt(new Date(new Date().getTime() + 7 * 24 * 3600 * 1000))
.withIssuer("hs")
.withJWTId(UUID.randomUUID().toString())
//设置签名算法和密钥
.sign(Algorithm.HMAC256("abcdefg"));

System.out.println(token);
}

(3)结果

1
2
3
4
5
6
7
8
9
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiJocyIsImxvZ2luIjp0cnVlLCJleHAiOjE2Mzg5NDk4NjMsImp0aSI6IjdiNThlMzgzLTQwMTEtNDA4ZS1hYjdlLTI0MjQzOWY1YTczNCIsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.
U8g5wOS2O83eEbVvH_bHMeHfVlSArktohoaID4cXGag



eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiJocyIsImxvZ2luIjp0cnVlLCJleHAiOjE2Mzg5NDk5OTMsImp0aSI6IjllN2IzMzRkLTc1YTItNDgxZi04YmY1LTE2Y2Y0NzEyMTFmNCIsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.
bz4cbX1l9Pf9c2FXT1P2NThwcuzxsTgIn13dZ-et_yg

我们可以看到,信息发生变化,token字符串生成的也发生了变化。

每次生成的token可能都不一样。

6、解析token(验签)

(1)token的合法性

(2)是否过期

(3)获取数据

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
/**
* 解析token
*/
@Test
public void test2(){
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("abcdefg")).build();

String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJocyIsImxvZ2luIjp0cnVlLCJleHAiOjE2Mzg5NDk5OTMsImp0aSI6IjllN2IzMzRkLTc1YTItNDgxZi04YmY1LTE2Y2Y0NzEyMTFmNCIsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.bz4cbX1l9Pf9c2FXT1P2NThwcuzxsTgIn13dZ-et_yg";

//验签
DecodedJWT decodedJWT = jwtVerifier.verify(token);

//通过,即可获取数据
// 获取头部
String header=decodedJWT.getHeader();
// 获取载荷
String payload=decodedJWT.getPayload();
// 获取签名
String signature=decodedJWT.getSignature();
System.out.println(header);
System.out.println(payload);
System.out.println(signature);

// 解析数据
Claim login = decodedJWT.getClaim("login");
System.out.println(login.asBoolean());
Claim username = decodedJWT.getClaim("username");
System.out.println(username.asString());
String id = decodedJWT.getId();
System.out.println(id);


}
赏

老板大气

支付宝
微信
  • Shiro

扫一扫,分享到微信

微信分享二维码
docker安装elk
elasticsearch学习笔记
  1. 1. Shiro安全框架
    1. 1.1. 一、Shiro概述
      1. 1.1.1. 1、Apache Shiro
      2. 1.1.2. 2、主要作用
      3. 1.1.3. 3、核心组件
        1. 1.1.3.1. (1)Subject(用户)
        2. 1.1.3.2. (2)SecurityManager(安全管理器)
        3. 1.1.3.3. (3)Realm(数据源)
    2. 1.2. 二、springboot整合shiro
      1. 1.2.1. 1、导入依赖
      2. 1.2.2. 2、配置shiro
      3. 1.2.3. 3、编写Realm
    3. 1.3. 三、身份认证和退出
      1. 1.3.1. 1、认证流程
      2. 1.3.2. 2、身份认证操作
      3. 1.3.3. 3、退出认证
      4. 1.3.4. 4、记住我
    4. 1.4. 四、授权
      1. 1.4.1. 1、过滤器授权(基于url拦截的权限管理)
      2. 1.4.2. 2、注解授权
    5. 1.5. 五、thymeleaf集成shiro标签
      1. 1.5.1. 1、作用
      2. 1.5.2. 2、导入依赖
      3. 1.5.3. 3、配置
      4. 1.5.4. 4、页面
      5. 1.5.5. 5、controller
      6. 1.5.6. 6、测试
    6. 1.6. 六、加密
      1. 1.6.1. 1、config
      2. 1.6.2. 2、realm
      3. 1.6.3. 3、数据库密码加密
      4. 1.6.4. 4、登录测试
    7. 1.7. 七、缓存管理
      1. 1.7.1. 1、作用
      2. 1.7.2. 2、操作
    8. 1.8. 八、会话管理
      1. 1.8.1. 1、shiro支持三种会话管理方式
      2. 1.8.2. 2、session会话管理
      3. 1.8.3. 3、配置
    9. 1.9. 九、分布式集群
      1. 1.9.1. 1、分布式集群会话共享
      2. 1.9.2. 2、操作
    10. 1.10. 十、前后端分离
      1. 1.10.1. 1、shiro会话跟踪
      2. 1.10.2. 2、解决方案:
      3. 1.10.3. 3、请求处理
      4. 1.10.4. 4、响应处理
      5. 1.10.5. 5、测试
    11. 1.11. 十一、JWT
      1. 1.11.1. 1、JWT
      2. 1.11.2. 2、会话跟踪
      3. 1.11.3. 3、JWT token字符串格式
        1. 1.11.3.1. (1)header(标头)
        2. 1.11.3.2. (2)payload(载荷)
        3. 1.11.3.3. (3)Signature(签名)
      4. 1.11.4. 4、JWT插件
      5. 1.11.5. 5、生成token(签名)
      6. 1.11.6. 6、解析token(验签)
© 2022 un1queyan
Hexo Theme Yilia by Litten 本站总访问量次
  • 所有文章
  • 友链
  • 关于我

tag:

  • java基础
  • java
  • 随笔
  • Java基础
  • JavaScript
  • mybatis
  • MySQL
  • Nginx
  • spring
  • Shiro
  • django
  • docker
  • elasticsearch
  • 计算机网络
  • linux
  • Linux
  • maven
  • mongodb
  • Django
  • python
  • sqlalchemy
  • redis
  • rabbitMQ
  • 算法练习
  • 设计模式
  • 数据结构
  • jvm
  • 计算机基础

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • Mybatis-Plus学习笔记

    2022-01-31

    #mybatis

  • mongodb学习笔记

    2022-01-16

    #mongodb

  • dao,dto,vo,po,pojo,bo的区别

    2021-12-06

    #设计模式

  • docker安装elk

    2021-11-10

  • Shiro安全框架

    2021-09-09

    #Shiro

  • elasticsearch学习笔记

    2021-07-14

    #elasticsearch

  • rabbitMQ学习笔记

    2021-07-11

    #rabbitMQ

  • JVM学习笔记

    2021-07-08

    #Java基础

  • Java学习笔记

    2021-07-08

    #Java基础

  • Mybatis

    2021-06-27

    #spring

  • springMVC源码

    2021-06-24

    #spring

  • springMVC框架

    2021-06-21

    #spring

  • 计算机网络

    2021-06-19

    #计算机基础

  • 微信小程序学习笔记

    2021-06-12

    #Django

  • maven

    2021-06-11

    #maven

  • MySQL高级

    2021-06-06

    #MySQL

  • HashMap源码

    2021-05-21

    #java基础

  • Java NIO

    2021-05-21

    #java基础

  • docker学习笔记

    2021-05-21

    #docker

  • JVM类加载过程

    2021-05-15

    #java

  • linux操作汇总

    2021-05-15

    #linux

  • osi七层模型

    2021-05-12

    #计算机网络

  • 项目准备

    2021-05-12

    #django

  • rest framework

    2021-05-09

    #django

  • python操作MySQL

    2021-05-08

    #sqlalchemy

  • 图片验证码

    2021-05-06

    #Django

  • MySQL基础总结

    2021-05-06

  • 随笔

    2021-05-05

  • Nginx笔记

    2021-04-28

    #Nginx

  • redis学习笔记

    2021-04-27

    #redis

  • Java中的逃逸分析

    2021-04-22

    #Java基础

  • python内存管理与垃圾回收机制

    2021-03-31

    #python

  • 几种常见的排序算法

    2021-03-30

    #算法练习

  • 建造者模式

    2021-03-15

    #设计模式

  • 抽象工厂模式

    2021-03-15

    #设计模式

  • 抽象工厂模式

    2021-03-14

    #设计模式

  • 工厂方法模式

    2021-03-13

    #设计模式

  • 简单工厂模式

    2021-03-12

    #设计模式

  • Linux下安装python

    2021-03-11

    #Linux

  • ajax

    2021-02-28

  • redis列表相关的操作

    2021-02-10

    #redis

  • redis哈希表相关的操作

    2021-02-10

    #redis

  • StringTable

    2021-02-09

    #java

  • redis字符串相关的操作

    2021-02-09

    #redis

  • 观察者模式

    2021-02-09

    #设计模式

  • Http请求的声明周期

    2021-01-08

    #随笔

  • redis哨兵

    2020-12-24

    #redis

  • 单例模式

    2020-12-24

    #设计模式

  • 类加载过程详解

    2020-12-24

    #jvm

  • redis主从同步

    2020-12-12

    #redis

  • 对象的实例化、内存布局和访问定位

    2020-10-19

    #Java基础

  • JVM解释器与编译器

    2020-10-18

    #Java基础

  • JVM运行时的数据结构

    2020-10-18

    #Java基础

  • orm基本操作

    2020-10-18

    #Django

  • JVM指令手册

    2020-10-17

    #java

  • float计算的底层原理

    2020-08-06

  • 2020-06-15

    #数据结构

  • JavaScript学习笔记

    2020-05-07

    #JavaScript

  • 编写指南

    2019-12-06

  • 友情链接1
  • 友情链接2
  • 友情链接3
  • 友情链接4
  • 友情链接5
  • 友情链接6
很惭愧

只做了一点微小的工作
谢谢大家