SpringSecurity
Spring Security开发配置
Reference
security原理 (opens new window)
流程说明:
- 客户端发起一个请求,进入 Security 过滤器链。
- 当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理。如果不是登出路径则直接进入下一个过滤器。
- 当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler ,登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。
- 进入认证BasicAuthenticationFilter进行用户认证,成功的话会把认证了的结果写入到SecurityContextHolder中SecurityContext的属性authentication上面。如果认证失败就会交给AuthenticationEntryPoint认证失败处理类,或者抛出异常被后续ExceptionTranslationFilter过滤器处理异常,如果是AuthenticationException就交给AuthenticationEntryPoint处理,如果是AccessDeniedException异常则交给AccessDeniedHandler处理。
- 当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层,否则到 AccessDeniedHandler 鉴权失败处理器处理。
Spring Security 实战干货:必须掌握的一些内置 Filter:https://blog.csdn.net/qq_35067322/article/details/102690579 (opens new window)
引入Security与jwt
首先我们导入security包,因为我们前后端交互用户凭证用的是JWT,所以我们也导入jwt的相关包。
- pom.xml
!-- 权限控制-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!-- hutool工具类--><dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
启动项目,这时候我们再去访问http://localhost:8081 (opens new window),会发现系统会先判断到你未登录跳转到http://localhost:8081/login (opens new window),因为security内置了登录页,用户名为user,密码在启动项目的时候打印在了控制台。登录完成之后我们才可以正常访问接口。因为每次启动密码都会改变,所以我们通过配置文件来配置一下默认的用户名和密码:
- application.yml
spring:
security:
user:
name: user
password: 111111
2
3
4
5
用户认证
首先我们来解决用户认证问题,分为首次登陆,和二次认证。
- 首次登录认证:用户名和密码完成登录
- 二次token认证:请求头携带Jwt进行身份认证
身份认证 - 登录认证
- 密码加密
使用Security内置的BCryptPasswordEncoder饼子啊SecurityConfig中进行配置:
/**
* 加密方式
*
* @return
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
2
3
4
5
6
7
8
9
- 重写UserDetailsService接口,实现数据库查询用户数据的过程
/**
* @ClassName SysUserDetailsService
* @Description 用户登录Service
* @Author hwzhao
* @Data 2022/7/22 17:01
**/
@Service
public class SysUserDetailsService implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
/**
* 根据用户名查用户信息
*
* @param username 用户名
* @return 用户详细信息
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.findUserByUserName(username);
if (sysUser != null){
SysUserDetails sysUserDetails = new SysUserDetails();
BeanUtils.copyProperties(sysUser, sysUserDetails);
Set<GrantedAuthority> authorities = new HashSet<>(); // 角色集合
//查询用户的角色
List<SysRole> roleList = sysUserService.findRoleByUserId(sysUserDetails.getId());
roleList.forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
});
sysUserDetails.setAuthorities(authorities);
return sysUserDetails;
}
return null;
}
}
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
因为security在认证用户身份的时候会调用UserDetailsService.loadUserByUsername()方法,因此我们重写了之后security就可以根据我们的流程去查库获取用户了。然后把UserDetailsServiceImpl注入到*UserAuthenticationProvider
*中:
cn.edu.buaa.security.UserAuthenticationProvider
/**
* @ClassName UserAuthenticationProvider
* @Description 用户登录验证处理类
* @Author hwzhao
* @Data 2022/7/23 10:52
* @Version 1.0
**/
@Component
@Transactional
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SysUserDetailsService userDetailsService;
@Autowired
private SysUserService sysUserService;
/**
* 身份验证
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (String) authentication.getPrincipal(); // 获取用户名
String password = (String) authentication.getCredentials(); // 获取密码
//这里先做的假登陆---处理用户登录逻辑
String[] userList = username.split("\\|\\|");
int role = Integer.parseInt(userList[0]);
username = userList[1];
SysUserDetails sysUserDetails = new SysUserDetails();
List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
if (role == 0) {
if (username.equals("root") && password.equals("123456")) {
sysUserDetails.setId(111111L);
sysUserDetails.setUsername("root");
sysUserDetails.setName("超级管理员");
grantedAuthorityList.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return "ROLE_ROOT";
}
});
} else {
throw new BadCredentialsException("用户名或密码错误" + "|" + username);
}
}
if (role == 1) {
NstrDepartment nstrDepartment = new NstrDepartment();
QueryWrapper<NstrDepartment> nstrDepartmentQueryWrapper = new QueryWrapper<>();
nstrDepartmentQueryWrapper.eq("nickname", username);
NstrDepartment item = nstrDepartment.selectOne(nstrDepartmentQueryWrapper);
if (item == null) {
throw new BadCredentialsException("用户名或密码错误" + "|" + username);
}
//这里需要检查下密码,先不做。
if (!password.equals("123456")) {
throw new BadCredentialsException("用户名或密码错误" + "|" + username);
}
sysUserDetails.setId(Long.parseLong(item.getId()));
sysUserDetails.setUsername(item.getNickname());
sysUserDetails.setName(item.getDepName());
grantedAuthorityList.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return "ROLE_ADMIN";
}
});
}
if (role == 2) {
CarsInstitution carsInstitutio = new CarsInstitution();
QueryWrapper<CarsInstitution> carsInstitutionQueryWrapper = new QueryWrapper<>();
carsInstitutionQueryWrapper.eq("nickname", username);
CarsInstitution item = carsInstitutio.selectOne(carsInstitutionQueryWrapper);
if (item == null) {
throw new BadCredentialsException("用户名或密码错误" + "|" + username);
}
//这里需要检查下密码,先不做。
if (!password.equals("123456")) {
throw new BadCredentialsException("用户名或密码错误" + "|" + username);
}
sysUserDetails.setId(Long.parseLong(item.getInsId()));
sysUserDetails.setUsername(item.getNickname());
sysUserDetails.setName(item.getInsName());
grantedAuthorityList.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return "ROLE_USER";
}
});
}
sysUserDetails.setAuthorities(grantedAuthorityList);
return new UsernamePasswordAuthenticationToken(sysUserDetails, password, sysUserDetails.getAuthorities());
}
/**
* 支持指定的身份验证
*/
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
然后上面UserDetailsService.loadUserByUsername()默认返回的UserDetails,我们自定义了*SysUserDetails
*去重写了UserDetails。
cn.edu.buaa.security.entity.SysUserDetails
@Data
public class SysUserDetails extends SysUser implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户角色
*/
private Collection<GrantedAuthority> authorities;
/**
* 账号是否过期
*/
private boolean isAccountNonExpired = false;
/**
* 账号是否锁定
*/
private boolean isAccountNonLocked = false;
/**
* 证书是否过期
*/
private boolean isCredentialsNonExpired = false;
/**
* 账号是否有效
*/
private boolean isEnabled = true;
/**
* 获得用户权限
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
/**
* 判断账号是否过期
*/
@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
/**
* 判断账号是否锁定
*/
@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
/**
* 判断证书是否过期
*/
@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
/**
* 判断账号是否有效
*/
@Override
public boolean isEnabled() {
return isEnabled;
}
}
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
身份认证 - jwt认证
登录成功之后前端就可以获取到了jwt的信息
service.interceptors.request.use(
config => {
// TODO:请求前的处理
if (getToken()) { // 在请求头中添加token
config.headers['Authorization'] = getToken()
}
return config
},
error => {
// TODO:错误处理
console.log(error);
return Promise.reject(error)
}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
所以后端进行用户身份识别的时候,我们需要通过请求头中获取jwt,然后解析出我们的用户名,这样我们就可以知道是谁在访问我们的接口啦,然后判断用户是否有权限等操作。
那么我们自定义一个过滤器用来进行识别jwt。
- JWTAuthenticationFilter
/**
* @ClassName JWTAuthenticationFilter
* @Description JWT权限过滤器,用于验证Token是否合法
* @Author hwzhao
* @Data 2022/7/23 17:48
* @Version 1.0
**/
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
// 取出Token
String token = request.getHeader(JWTConfig.tokenHeader);
if (token != null && token.startsWith(JWTConfig.tokenPrefix)) {
SysUserDetails sysUserDetails = JWTTokenUtil.parseAccessToken(token);
if (sysUserDetails != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
sysUserDetails, sysUserDetails.getId(), sysUserDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
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
上面的逻辑也很简单,正如我前面说到的,获取到用户名之后我们直接把封装成UsernamePasswordAuthenticationToken,之后交给SecurityContextHolder参数传递authentication对象,这样后续security就能获取到当前登录的用户信息了,也就完成了用户认证。【当认证失败的时候会进入AuthenticationEntryPoint,于是我们自定义认证失败返回的数据】
自定义UserLoginFailureHandler
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
ResponseUtils.responseJson(response, ResponseUtils.response(500, "登录失败", exception.getMessage().split("\\|")[0]));
}
}
2
3
4
5
6
7
自定义UserLoginSuccessHandler
/**
* @ClassName UserLoginSuccessHandler
* @Description 登录成功处理类
* @Author hwzhao
* @Data 2022/7/22 19:00
**/
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
SysUserDetails sysUserDetails = (SysUserDetails) authentication.getPrincipal();
String token = JWTTokenUtil.createAccessToken(sysUserDetails);
Map<String, Object> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("id", sysUserDetails.getId());
GrantedAuthority role = sysUserDetails.getAuthorities().iterator().next();
tokenMap.put("role", role.getAuthority().replace("ROLE_", ""));
tokenMap.put("name", sysUserDetails.getName());
ResponseUtils.responseJson(response, ResponseUtils.response(200, "登录成功", tokenMap));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
JWTTokenUtil
public class JWTTokenUtil {
/**
* 创建Token
*
* @param sysUserDetails 用户信息
* @return
*/
public static String createAccessToken(SysUserDetails sysUserDetails) {
String token = Jwts.builder().setId(// 设置JWT
sysUserDetails.getId().toString()) // 用户Id
.setSubject(sysUserDetails.getName()) // 主题
.setIssuedAt(new Date()) // 签发时间
.setIssuer("zhw") // 签发者
.setExpiration(new Date(System.currentTimeMillis() + JWTConfig.expiration)) // 过期时间
.signWith(SignatureAlgorithm.HS512, JWTConfig.secret) // 签名算法、密钥
.claim("authorities", JSON.toJSONString(sysUserDetails.getAuthorities())).compact(); // 自定义其他属性,如用户组织机构ID,用户所拥有的角色,用户权限信息等
return JWTConfig.tokenPrefix + token;
}
/**
* 解析Token
*
* @param token Token信息
* @return
*/
public static SysUserDetails parseAccessToken(String token) {
System.out.println(token);
SysUserDetails sysUserDetails = null;
if (StringUtils.isNotEmpty(token)) {
try {
// 去除JWT前缀
token = token.substring(JWTConfig.tokenPrefix.length());
// 解析Token
Claims claims = Jwts.parser().setSigningKey(JWTConfig.secret).parseClaimsJws(token).getBody();
// 获取用户信息
sysUserDetails = new SysUserDetails();
sysUserDetails.setId(Long.parseLong(claims.getId()));
sysUserDetails.setName(claims.getSubject());
// 获取角色
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
String authority = claims.get("authorities").toString();
System.out.println(authority);
if (StringUtils.isNotEmpty(authority)) {
List<Map<String, String>> authorityList = JSON.parseObject(authority,
new TypeReference<List<Map<String, String>>>() {
});
for (Map<String, String> role : authorityList) {
if (!role.isEmpty()) {
authorities.add(new SimpleGrantedAuthority(role.get("authority")));
}
}
}
sysUserDetails.setAuthorities(authorities);
} catch (Exception e) {
e.printStackTrace();
log.error("解析Token异常:" + e);
}
}
return sysUserDetails;
}
}
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
登录成功之后我们利用用户名生成jwt,jwtUtils这个工具类我就不贴代码了哈,去看我们项目源码,然后把jwt作为请求头返回回去,名称就叫Authorization哈。我们需要在配置文件中配置一些jwt的一些密钥信息:
- application.yml
# JWT配置
jwt:
# 密匙KEY
secret: JWTshengou
# HeaderKEY
tokenHeader: Authorization
# Token前缀字符
tokenPrefix: BJ-
# 过期时间 单位秒 1天后过期=86400 7天后过期=604800
expiration: 86400
# 配置不需要认证的接口
antMatchers: /login**,/favicon.ico,/user/insert**,/user/userExist**
2
3
4
5
6
7
8
9
10
11
12
配置SecurityConfig
- SecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法权限注解
public class SysSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
MyAuthenticationFilter authenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
/**
* 无权限处理类
*/
@Autowired
private UserAccessDeniedHandler userAccessDeniedHandler;
/**
* 用户未登录处理类
*/
@Autowired
private UserNotLoginHandler userNotLoginHandler;
/**
* 用户登录成功处理类
*/
@Autowired
private UserLoginSuccessHandler userLoginSuccessHandler;
/**
* 用户登录失败处理类
*/
@Autowired
private UserLoginFailureHandler userLoginFailureHandler;
/**
* 用户登出成功处理类
*/
@Autowired
private UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 用户登录验证
*/
@Autowired
private UserAuthenticationProvider userAuthenticationProvider;
/**
* 用户权限注解
*/
@Autowired
private UserPermissionEvaluator userPermissionEvaluator;
/**
* 加密方式
*
* @return
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 注入自定义PermissionEvaluator
*
* @return
*/
@Bean
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(userPermissionEvaluator);
return handler;
}
/**
* 用户登录验证
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(userAuthenticationProvider);
}
/**
* 安全权限配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 权限配置
.antMatchers(JWTConfig.antMatchers.split(",")).permitAll()// 获取白名单(不进行权限验证)
.anyRequest().authenticated()// 其他的需要登陆后才能访问
.and().httpBasic().authenticationEntryPoint(userNotLoginHandler) // 配置未登录处理类
.and().formLogin().loginProcessingUrl("/login/submit")// 配置登录URL
.successHandler(userLoginSuccessHandler) // 配置登录成功处理类
.failureHandler(userLoginFailureHandler) // 配置登录失败处理类
.and().logout().logoutUrl("/logout/submit")// 配置登出地址
.logoutSuccessHandler(userLogoutSuccessHandler) // 配置用户登出处理类
.and().exceptionHandling().accessDeniedHandler(userAccessDeniedHandler)// 配置没有权限处理类
.and().cors()// 开启跨域
.and().csrf().disable(); // 禁用跨站请求伪造防护
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用session(使用Token认证)
http.headers().cacheControl(); // 禁用缓存
http.addFilter(new JWTAuthenticationFilter(authenticationManager())); //// 添加JWT过滤器
}
}
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
解决授权
然后关于权限部分,也是security的重要功能,当用户认证成功之后,我们就知道谁在访问系统接口,这是又有一个问题,就是这个用户有没有权限来访问我们这个接口呢,要解决这个问题,我们需要知道用户有哪些权限,哪些角色,这样security才能我们做权限判断。
之前我们已经定义及几张表,用户、角色、菜单、以及一些关联表,一般当权限粒度比较细的时候,我们都通过判断用户有没有此菜单或操作的权限,而不是通过角色判断,而用户和菜单是不直接做关联的,是通过用户拥有哪些角色,然后角色拥有哪些菜单权限这样来获得的。
问题1:我们是在哪里赋予用户权限的?有两个地方:
- 1、用户登录,调用调用UserDetailsService.loadUserByUsername()方法时候可以返回用户的权限信息。
- 2、接口调用进行身份认证过滤器时候JWTAuthenticationFilter,需要返回用户权限信息
问题2:在哪里决定什么接口需要什么权限?
Security内置的权限注解:
- @PreAuthorize (opens new window):方法执行前进行权限检查
- @PostAuthorize (opens new window):方法执行后进行权限检查
- @Secured (opens new window):类似于 @PreAuthorize (opens new window)
可以在Controller的方法前添加这些注解表示接口需要什么权限。
比如需要Admin角色权限:
@PreAuthorize("hasRole('admin')")
比如需要添加管理员的操作权限
@PreAuthorize("hasAuthority('sys:user:save')") //sys:user:save为对应数据库中自己设置的参数权限名称
验证权限的流程:
- 用户登录或者调用接口时候识别到用户,并获取到用户的权限信息
- 注解标识Controller中的方法需要的权限或角色
- Security通过FilterSecurityInterceptor匹配URI和权限是否匹配
- 有权限则可以访问接口,当无权限的时候返回异常交给AccessDeniedHandler操作类处理
- SysUserDetailsService
@Service
public class SysUserDetailsService implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
/**
* 根据用户名查用户信息
*
* @param username 用户名
* @return 用户详细信息
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.findUserByUserName(username);
if (sysUser != null){
SysUserDetails sysUserDetails = new SysUserDetails();
BeanUtils.copyProperties(sysUser, sysUserDetails);
Set<GrantedAuthority> authorities = new HashSet<>(); // 角色集合
//查询用户的角色
List<SysRole> roleList = sysUserService.findRoleByUserId(sysUserDetails.getId());
roleList.forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
});
sysUserDetails.setAuthorities(authorities);
return sysUserDetails;
}
return null;
}
}
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
- JWTAuthenticationFilter
/**
* @ClassName JWTAuthenticationFilter
* @Description JWT权限过滤器,用于验证Token是否合法
* @Author hwzhao
* @Data 2022/7/23 17:48
* @Version 1.0
**/
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
// 取出Token
String token = request.getHeader(JWTConfig.tokenHeader);
if (token != null && token.startsWith(JWTConfig.tokenPrefix)) {
SysUserDetails sysUserDetails = JWTTokenUtil.parseAccessToken(token);
if (sysUserDetails != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
sysUserDetails, sysUserDetails.getId(), sysUserDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
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
然后sysUserService.findRoleByUserId(sysUserDetails.getId());
因为要查询数据库,具体SQL如下:
<!-- 根据用户ID查询角色 -->
<select id="findRoleByUserId" resultType="cn.edu.buaa.entity.SysRole" parameterType="long">
SELECT r.*
FROM sys_role r
LEFT JOIN sys_user_role ur ON ur.role_id = r.id
WHERE ur.user_id = #{userId}
</select>
<!-- 根据用户ID查询权限 -->
<select id="findAuthByUserId" resultType="cn.edu.buaa.entity.SysAuth" parameterType="long">
SELECT a.*
FROM sys_auth a
LEFT JOIN sys_role_auth ra ON ra.auth_id = a.id
LEFT JOIN sys_user_role ur ON ur.role_id = ra.role_id
WHERE ur.user_id = #{userId}
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
无权限数据返回
- com.markerhub.security.JwtAccessDeniedHandler
/**
* @ClassName UserAccessDeniedHandler
* @Description 无权限处理类
* @Author hwzhao
* @Data 2022/7/23 22:39
**/
@Component
public class UserAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
// //写入登陆时间
// LoginRecord loginRecord = new LoginRecord();
// loginRecord.setUsername(accessDeniedException.getMessage().split("\\|")[1]);
// loginRecord.setLoginType("登陆");
// loginRecord.setLoginStatus("拒绝访问");
// loginRecord.setCreateTime(LocalDateTime.now(Clock.system(ZoneId.of("Asia/Shanghai"))).toString());
// loginRecord.insert();
ResponseUtils.responseJson(response, ResponseUtils.response(403, "拒绝访问", accessDeniedException.getMessage().split("\\|")[0]));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
跨域
/**
* @ClassName CorsConfig
* @Description AJAX请求跨域
* @Author hwzhao
* @Data 2022/7/23 16:53
* @Version 1.0
**/
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addExposedHeader("Authorization");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.exposedHeaders("Authorization")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("access-control-allow-headers",
"access-control-allow-methods",
"access-control-allow-origin",
"access-control-max-age",
"X-Frame-Options")
.maxAge(3600);
}
}
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