Spring Security是Spring提供的一個安全框架,提供認證和授權功能,最主要的是它提供了簡單的使用方式,同時又有很高的靈活性,簡單,靈活,強大。
我個人博客系統採用的權限框架就是Spring Security,正好整合到SpringCloud裏面。
一般系統里關於角色方面通常有這麼幾張表,角色表、用戶-角色表、菜單表、角色-菜單表等。
不過我個人博客系統主要以wordpress作為參考,沿用其12張表,如圖:
一、導入Maven依賴
<properties>
<jjwt.version>0.9.0</jjwt.version>
<spring-security-jwt.version>1.0.9.RELEASE</spring-security-jwt.version>
</properties>
<!-- springsecurity-->
<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>${spring-security-jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
二、編寫Spring Security配置類
package com.springcloud.blog.admin.config;
import com.springcloud.blog.admin.security.UserAuthenticationProvider;
import com.springcloud.blog.admin.security.UserPermissionEvaluator;
import com.springcloud.blog.admin.security.handler.*;
import com.springcloud.blog.admin.security.jwt.JWTAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
/**
* SpringSecurity配置類
* @Author youcong
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //開啟權限註解,默認是關閉的
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定義登錄成功處理器
*/
@Autowired
private UserLoginSuccessHandler userLoginSuccessHandler;
/**
* 自定義登錄失敗處理器
*/
@Autowired
private UserLoginFailureHandler userLoginFailureHandler;
/**
* 自定義註銷成功處理器
*/
@Autowired
private UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 自定義暫無權限處理器
*/
@Autowired
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
/**
* 自定義未登錄的處理器
*/
@Autowired
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
/**
* 自定義登錄邏輯驗證器
*/
@Autowired
private UserAuthenticationProvider userAuthenticationProvider;
/**
* 加密方式
* @Author youcong
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 注入自定義PermissionEvaluator
*/
@Bean
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler(){
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new UserPermissionEvaluator());
return handler;
}
/**
* 配置登錄驗證邏輯
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
//這裏可啟用我們自己的登陸驗證邏輯
auth.authenticationProvider(userAuthenticationProvider);
}
/**
* 配置security的控制邏輯
* @Author youcong
* @Param http 請求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 不進行權限驗證的請求或資源(從配置文件中讀取)
.antMatchers(JWTConfig.antMatchers.split(",")).permitAll()
// .antMatchers("/*").permitAll()
// 其他的需要登陸后才能訪問
.anyRequest().authenticated()
.and()
// 配置未登錄自定義處理類
.httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
.and()
// 配置登錄地址
.formLogin()
.loginProcessingUrl("/login/userLogin")
// 配置登錄成功自定義處理類
.successHandler(userLoginSuccessHandler)
// 配置登錄失敗自定義處理類
.failureHandler(userLoginFailureHandler)
.and()
// 配置登出地址
.logout()
.logoutUrl("/login/userLogout")
// 配置用戶登出自定義處理類
.logoutSuccessHandler(userLogoutSuccessHandler)
.and()
// 配置沒有權限自定義處理類
.exceptionHandling().accessDeniedHandler(userAuthAccessDeniedHandler)
.and()
// 開啟跨域
.cors()
.and()
// 取消跨站請求偽造防護
.csrf().disable();
// 基於Token不需要session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用緩存
http.headers().cacheControl();
// 添加JWT過濾器
http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
}
}
三、編寫JWTConfig和application.yml增加jwt相關配置
package com.springcloud.blog.admin.config;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* JWT配置類
* @Author youcong
*/
@Getter
@Component
@ConfigurationProperties(prefix = "jwt")
public class JWTConfig {
/**
* 密鑰KEY
*/
public static String secret;
/**
* TokenKey
*/
public static String tokenHeader;
/**
* Token前綴字符
*/
public static String tokenPrefix;
/**
* 過期時間
*/
public static Integer expiration;
/**
* 不需要認證的接口
*/
public static String antMatchers;
public void setSecret(String secret) {
this.secret = secret;
}
public void setTokenHeader(String tokenHeader) {
this.tokenHeader = tokenHeader;
}
public void setTokenPrefix(String tokenPrefix) {
this.tokenPrefix = tokenPrefix;
}
public void setExpiration(Integer expiration) {
this.expiration = expiration * 1000;
}
public void setAntMatchers(String antMatchers) {
this.antMatchers = antMatchers;
}
}
application.yml增加如下內容:
# JWT配置
jwt:
# 密匙KEY
secret: JWTSecret
# HeaderKEY
tokenHeader: Authorization
# Token前綴字符
tokenPrefix: challenger-
# 過期時間 單位秒 1天後過期=86400 7天後過期=604800
expiration: 86400
# 配置不需要認證的接口
antMatchers: /index/**,/login/**,/favicon.ico
# 有效時間
validTime: 7
四、編寫過濾器處理類
1.UserLoginSuccessHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.config.JWTConfig;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.utils.AccessAddressUtil;
import com.springcloud.blog.admin.utils.JWTTokenUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @Description 登錄成功處理類
* @Author youcong
*/
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
/**
* 登錄成功返回結果
* @Author youcong
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
// 組裝JWT
SelfUserEntity selfUserEntity = (SelfUserEntity) authentication.getPrincipal();
String token = JWTTokenUtil.createAccessToken(selfUserEntity);
token = JWTConfig.tokenPrefix + token;
// 封裝返回參數
Map<String,Object> resultData = new HashMap<>();
resultData.put("code","200");
resultData.put("msg", "登錄成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
}
}
2.UserLoginFailureHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description 登錄失敗處理類
* @Author youcong
*/
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登錄失敗返回結果
* @Author youcong
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
// 這些對於操作的處理類可以根據不同異常進行不同處理
if (exception instanceof UsernameNotFoundException){
System.out.println("【登錄失敗】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用戶名不存在"));
}
if (exception instanceof LockedException){
System.out.println("【登錄失敗】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用戶被凍結"));
}
if (exception instanceof BadCredentialsException){
System.out.println("【登錄失敗】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"密碼錯誤"));
}
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"登錄失敗"));
}
}
3.UserLogoutSuccessHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.utils.DateUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* 登出成功處理類
* @Author youcong
*/
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
/**
* 用戶登出返回結果
* 這裏應該讓前端清除掉Token
* @Author youcong
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
Map<String,Object> resultData = new HashMap<>();
resultData.put("code","200");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext();
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
}
}
4.UserAuthAccessDeniedHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description 暫無權限處理類
* @Author youcong
*/
@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler {
/**
* 暫無權限返回結果
* @Author youcong
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(403,"未授權"));
}
}
5.UserAuthenticationEntryPointHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 用戶未登錄處理類
* @Author youcong
*/
@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
/**
* 用戶未登錄返回結果
* @Author youcong
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(401,"未登錄"));
}
}
6.UserAuthenticationProvider.java
自定義登錄驗證這個類,需要根據實際情況重寫。通常來說改動不大。
package com.springcloud.blog.admin.security;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Usermeta;
import com.springcloud.blog.admin.entity.Users;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.service.UsermetaService;
import com.springcloud.blog.admin.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 自定義登錄驗證
*
* @Author youcong
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UsersService usersService;
@Autowired
private UsermetaService usermetaService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 獲取表單輸入中返回的用戶名
String userName = (String) authentication.getPrincipal();
// 獲取表單中輸入的密碼
String password = (String) authentication.getCredentials();
// 查詢用戶是否存在
SelfUserEntity userInfo = usersService.getUserInfo(userName);
if (userInfo.getUsername() == null || userInfo.getUsername() == "") {
throw new UsernameNotFoundException("用戶名不存在");
}
// 我們還要判斷密碼是否正確,這裏我們的密碼使用BCryptPasswordEncoder進行加密的
if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
throw new BadCredentialsException("密碼不正確");
}
// 還可以加一些其他信息的判斷,比如用戶賬號已停用等判斷
if (userInfo.getStatus().equals("1")) {
throw new LockedException("該用戶已被凍結");
}
// 角色集合
Set<GrantedAuthority> authorities = new HashSet<>();
EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
roleWrapper.eq("user_id",userInfo.getUserId());
roleWrapper.eq("meta_key","wp_user_level");
// 查詢用戶角色
List<Usermeta> sysRoleEntityList = usermetaService.selectList(roleWrapper);
for (Usermeta sysRoleEntity: sysRoleEntityList){
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getMetaValue()));
}
userInfo.setAuthorities(authorities);
// 進行登錄
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
7.UserPermissionEvaluator.java
package com.springcloud.blog.admin.security;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Usermeta;
import com.springcloud.blog.admin.service.UsermetaService;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 自定義權限註解驗證
* @Author youcong
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private UsermetaService usermetaService;
/**
* hasPermission鑒權方法
* 這裏僅僅判斷PreAuthorize註解中的權限表達式
* 實際中可以根據業務需求設計數據庫通過targetUrl和permission做更複雜鑒權
* 當然targetUrl不一定是URL可以是數據Id還可以是管理員標識等,這裏根據需求自行設計
* @Author youcong
* @Param authentication 用戶身份(在使用hasPermission表達式時Authentication參數默認會自動帶上)
* @Param targetUrl 請求路徑
* @Param permission 請求路徑權限
* @Return boolean 是否通過
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 獲取用戶信息
Usermeta selfUserEntity =(Usermeta) authentication.getPrincipal();
// 查詢用戶權限(這裏可以將權限放入緩存中提升效率)
Set<String> permissions = new HashSet<>();
EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
roleWrapper.eq("user_id",selfUserEntity.getUserId());
roleWrapper.eq("meta_key","wp_user_level");
List<Usermeta> sysMenuEntityList = usermetaService.selectList(roleWrapper);
for (Usermeta sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getMetaValue());
}
// 權限對比
if (permissions.contains(permission.toString())){
return true;
}
return true;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
五、編寫實體類
package com.springcloud.blog.admin.security.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
/**
* SpringSecurity用戶的實體
* 注意:這裏必須要實現UserDetails接口
*
* @Author youcong
*/
public class SelfUserEntity implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
/**
* 用戶ID
*/
private Long userId;
/**
* 用戶名
*/
private String username;
/**
* 密碼
*/
private String password;
/**
* 狀態
*/
private String status;
/**
* 显示名稱
*/
private String displayName;
/**
* 用戶參數
*/
private Map<String, String> userParamMap;
/**
* 用戶角色
*/
private Collection<GrantedAuthority> authorities;
/**
* 賬戶是否過期
*/
private boolean isAccountNonExpired = false;
/**
* 賬戶是否被鎖定
*/
private boolean isAccountNonLocked = false;
/**
* 證書是否過期
*/
private boolean isCredentialsNonExpired = false;
/**
* 賬戶是否有效
*/
private boolean isEnabled = true;
public static long getSerialVersionUID() {
return serialVersionUID;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Map<String, String> getUserParamMap() {
return userParamMap;
}
public void setUserParamMap(Map<String, String> userParamMap) {
this.userParamMap = userParamMap;
}
@Override
public Collection<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;
}
}
六、編寫JWT接口請求攔截器
package com.springcloud.blog.admin.security.jwt;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.springcloud.blog.admin.config.JWTConfig;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.utils.CollectionUtil;
import com.springcloud.blog.admin.utils.JWTTokenUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* JWT接口請求校驗攔截器
* 請求接口時會進入這裏驗證Token是否合法和過期
*
* @Author youcong
*/
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {
public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 獲取請求頭中JWT的Token
String tokenHeader = request.getHeader(JWTConfig.tokenHeader);
if (null != tokenHeader && tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
try {
// 截取JWT前綴
String token = tokenHeader.replace(JWTConfig.tokenPrefix, "");
// 解析JWT
Claims claims = Jwts.parser()
.setSigningKey(JWTConfig.secret)
.parseClaimsJws(token)
.getBody();
// 獲取用戶名
String username = claims.getSubject();
String userId = claims.getId();
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(userId)) {
// 獲取角色
List<GrantedAuthority> authorities = new ArrayList<>();
String authority = claims.get("authorities").toString();
if (!StringUtils.isEmpty(authority)) {
List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
for (Map<String, String> role : authorityMap) {
if (!StringUtils.isEmpty(role)) {
authorities.add(new SimpleGrantedAuthority(role.get("authority")));
}
}
}
//組裝參數
SelfUserEntity selfUserEntity = new SelfUserEntity();
selfUserEntity.setUsername(claims.getSubject());
selfUserEntity.setUserId(Long.parseLong(claims.getId()));
selfUserEntity.setAuthorities(authorities);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException e) {
System.out.println("Token過期");
} catch (Exception e) {
System.out.println("Token無效");
}
}
filterChain.doFilter(request, response);
return;
}
}
七、SpringSecurity用戶的業務實現
package com.springcloud.blog.admin.security.service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Users;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.service.UsersService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
* SpringSecurity用戶的業務實現
*
* @Author youcong
*/
@Component
public class SelfUserDetailsService implements UserDetailsService {
@Autowired
private UsersService usersService;
/**
* 查詢用戶信息
*
* @Author youcong
* @Param username 用戶名
* @Return UserDetails SpringSecurity用戶信息
*/
@Override
public SelfUserEntity loadUserByUsername(String username) throws UsernameNotFoundException {
EntityWrapper<Users> wrapper = new EntityWrapper<>();
//郵箱正則表達式
String expr = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$";
//是否為郵箱
if (username.matches(expr)) {
wrapper.eq("user_email", username);
} else {
wrapper.eq("user_login", username);
}
// 查詢用戶信息
Users sysUserEntity = usersService.selectOne(wrapper);
if (sysUserEntity != null) {
// 組裝參數
SelfUserEntity selfUserEntity = new SelfUserEntity();
BeanUtils.copyProperties(sysUserEntity, selfUserEntity);
return selfUserEntity;
}
return null;
}
}
八、Spring Security常用註解
1.@Secured
當@EnableGlobalMethodSecurity(securedEnabled=true)的時候,@Secured可以使用。
@PostMapping("/helloUser")
@Secured({"ROLE_normal","ROLE_admin"})
public Map<String, Object> initDashboard() {
Map<String, Object> result = new HashMap<>();
result.put(ResponseDict.RESPONSE_TITLE_KEY, "儀錶盤初始化");
result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
return ResultUtil.resultSuccess(result);
}
說明:擁有normal或者admin角色的用戶都可以方法helloUser()方法。另外需要注意的是這裏匹配的字符串需要添加前綴“ROLE_“。
2.@PreAuthorize
Spring的 @PreAuthorize/@PostAuthorize 註解更適合方法級的安全,也支持Spring 表達式語言,提供了基於表達式的訪問控制。
當@EnableGlobalMethodSecurity(prePostEnabled=true)的時候,@PreAuthorize可以使用:
@PostMapping("/initDashboard")
@PreAuthorize("hasRole('100')")
public Map<String, Object> initDashboard() {
Map<String, Object> result = new HashMap<>();
result.put(ResponseDict.RESPONSE_TITLE_KEY, "儀錶盤初始化");
result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
return ResultUtil.resultSuccess(result);
}
3.@PostAuthorize
@PostAuthorize 註解使用並不多,在方法執行后再進行權限驗證,適合驗證帶有返回值的權限,Spring EL 提供 返回對象能夠在表達式語言中獲取返回的對象returnObject。
當@EnableGlobalMethodSecurity(prePostEnabled=true)的時候,@PostAuthorize可以使用:
@GetMapping("/getUserInfo")
@PostAuthorize(" returnObject!=null && returnObject.username == authentication.name")
public User getUserInfo() {
Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user;
if("anonymousUser".equals(pricipal)) {
user = null;
}else {
user = (User) pricipal;
}
return user;
}
九、測試
(1)登錄測試,拿到token,如圖:
(2)請求中如果不攜帶token的話,請求其它接口就會显示沒有登錄的提示,如圖:
(3)正確的請求應當攜帶token,就像下面這樣,如圖:
(4)沒有權限請求,如圖:
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
※產品缺大量曝光嗎?你需要的是一流包裝設計!