SpringBoot 整合SpringSecurity示例實現前後分離權限註解+JWT登錄認證

20大進階架構專題每日送達

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

juejin.im/post/5da82f066fb9a04e2a73daec

一.說明

SpringSecurity是一個用於Java 企業級應用程序的安全框架,主要包含用戶認證和用戶授權兩個方面.相比較Shiro而言,Security功能更加的強大,它可以很容易地擴展以滿足更多安全控制方面的需求,但也相對它的學習成本會更高,兩種框架各有利弊.實際開發中還是要根據業務和項目的需求來決定使用哪一種.

JWT是在Web應用中安全傳遞信息的規範,從本質上來說是Token的演變,是一種生成加密用戶身份信息的Token,特別適用於分佈式單點登陸的場景,無需在服務端保存用戶的認證信息,而是直接對Token進行校驗獲取用戶信息,使單點登錄更為簡單靈活.

二.項目環境

  • SpringBoot版本:2.1.6

  • SpringSecurity版本: 5.1.5

  • MyBatis-Plus版本: 3.1.0

  • JDK版本:1.8

  • 數據表(SQL文件在項目中):數據庫中測試號的密碼進行了加密,密碼皆為123456

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

Maven依賴如下:

<code><dependencies>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-web/<artifactid>
/<dependency>
<dependency>
<groupid>mysql/<groupid>
<artifactid>mysql-connector-java/<artifactid>
<scope>runtime/<scope>
/<dependency>
<dependency>
<groupid>org.projectlombok/<groupid>
<artifactid>lombok/<artifactid>
<optional>true/<optional>
/<dependency>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-test/<artifactid>
<scope>test/<scope>
/<dependency>

<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-security/<artifactid>
/<dependency>

<dependency>
<groupid>com.baomidou/<groupid>
<artifactid>mybatis-plus-boot-starter/<artifactid>
<version>3.1.0/<version>
/<dependency>

<dependency>
<groupid>com.alibaba/<groupid>
<artifactid>druid/<artifactid>
<version>1.1.6/<version>
/<dependency>

<dependency>
<groupid>org.apache.commons/<groupid>
<artifactid>commons-lang3/<artifactid>
<version>3.5/<version>

/<dependency>

<dependency>
<groupid>com.alibaba/<groupid>
<artifactid>fastjson/<artifactid>
<version>1.2.45/<version>
/<dependency>

<dependency>
<groupid>org.springframework.security/<groupid>
<artifactid>spring-security-jwt/<artifactid>
<version>1.0.9.RELEASE/<version>
/<dependency>
<dependency>
<groupid>io.jsonwebtoken/<groupid>
<artifactid>jjwt/<artifactid>
<version>0.9.0/<version>
/<dependency>
/<dependencies>
/<code>

配置如下:

<code># 配置端口
server:
port: 8764
spring:
# 配置數據源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sans_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertTo&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
# JWT配置
jwt:
# 密匙KEY
secret: JWTSecret
# HeaderKEY
tokenHeader: Authorization
# Token前綴字符
tokenPrefix: Sans-
# 過期時間 單位秒 1天后過期=86400 7天后過期=604800
expiration: 86400

# 配置不需要認證的接口
antMatchers: /index,/login/**,/favicon.ico
# Mybatis-plus相關配置
mybatis-plus:
# xml掃描,多個目錄用逗號或者分號分隔(告訴 Mapper 所對應的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默認值,可以不設置
global-config:
db-config:
#主鍵類型 AUTO:"數據庫ID自增" INPUT:"用戶輸入ID",ID_WORKER:"全局唯一ID (數字類型唯一ID)", UUID:"全局唯一ID UUID";
id-type: AUTO
#字段策略 IGNORED:"忽略判斷" NOT_:"非 判斷") NOT_EMPTY:"非空判斷"
field-strategy: NOT_EMPTY
#數據庫類型
db-type: MYSQL
configuration:
# 是否開啟自動駝峰命名規則映射:從數據庫列名到Java屬性駝峰命名的類似映射
map-underscore-to-camel-case: true
# 返回map時true:當查詢數據為空時字段返回為,false:不加這個查詢數據為空時,字段將被隱藏
call-setters-on-s: true
# 這個配置會將執行的sql打印出來,在開發或測試的時候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
/<code>

三.編寫項目基礎類

Entity,Dao,Service,及等SpringSecurity用戶的Entity,Service類等在這裡省略,請參考源碼

編寫JWT工具類

<code>/**
* JWT工具類
* @Author Sans
* @CreateTime 2019/10/2 7:42
*/
@Slf4j
public class JWTTokenUtil {

/**
* 生成Token
* @Author Sans
* @CreateTime 2019/10/2 12:16
* @Param selfUserEntity 用戶安全實體
* @Return Token
*/
public static String createAccessToken(SelfUserEntity selfUserEntity){
// 登陸成功生成JWT
String token = Jwts.builder
// 放入用戶名和用戶ID
.setId(selfUserEntity.getUserId+"")
// 主題
.setSubject(selfUserEntity.getUsername)
// 簽發時間
.setIssuedAt(new Date)
// 簽發者
.setIssuer("sans")
// 自定義屬性 放入用戶擁有權限
.claim("authorities", JSON.toJSONString(selfUserEntity.getAuthorities))
// 失效時間
.setExpiration(new Date(System.currentTimeMillis + JWTConfig.expiration))
// 簽名算法和密鑰
.signWith(SignatureAlgorithm.HS512, JWTConfig.secret)
.compact;
return token;
}
}
/<code>

編寫暫無權限處理類

<code>/**
* @Description 暫無權限處理類
* @Author Sans
* @CreateTime 2019/10/3 8:39
*/
@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler{
/**
* 暫無權限返回結果
* @Author Sans
* @CreateTime 2019/10/3 8:41
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(403,"未授權"));
}
}
/<code>

編寫用戶未登錄處理類

<code>/**
* 用戶未登錄處理類
* @Author Sans
* @CreateTime 2019/10/3 8:55
*/
@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
/**
* 用戶未登錄返回結果
* @Author Sans
* @CreateTime 2019/10/3 9:01
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(401,"未登錄"));
}
}
/<code>

編寫登錄失敗處理類

<code>/**
* @Description 登錄失敗處理類

* @Author Sans
* @CreateTime 2019/10/3 9:06
*/
@Slf4j
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登錄失敗返回結果
* @Author Sans
* @CreateTime 2019/10/3 9:12
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
// 這些對於操作的處理類可以根據不同異常進行不同處理
if (exception instanceof UsernameNotFoundException){
log.info("【登錄失敗】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用戶名不存在"));
}
if (exception instanceof LockedException){
log.info("【登錄失敗】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用戶被凍結"));
}
if (exception instanceof BadCredentialsException){
log.info("【登錄失敗】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用戶名密碼不正確"));
}
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"登錄失敗"));
}
}
/<code>

編寫登錄成功處理類

<code>/**
* @Description 登錄成功處理類
* @Author Sans
* @CreateTime 2019/10/3 9:13
*/
@Slf4j
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
/**

* 登錄成功返回結果
* @Author Sans
* @CreateTime 2019/10/3 9:27
*/
@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> resultData = new HashMap<>;
resultData.put("code","200");
resultData.put("msg", "登錄成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
}
}
/<string>/<code>

編寫登出成功處理類

<code>/**
* 用戶登出類
* @Author Sans
* @CreateTime 2019/10/3 9:42
*/
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
/**
* 用戶登出返回結果
* 這裡應該讓前端清除掉Token
* @Author Sans
* @CreateTime 2019/10/3 9:50
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
Map<string> resultData = new HashMap<>;
resultData.put("code","200");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext;
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
}
}
/<string>/<code>

四.編寫Security核心類

編寫自定義登錄驗證類

<code>/**
* 自定義登錄驗證
* @Author Sans
* @CreateTime 2019/10/1 19:11
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SelfUserDetailsService selfUserDetailsService;
@Autowired
private SysUserService sysUserService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 獲取表單輸入中返回的用戶名
String userName = (String) authentication.getPrincipal;
// 獲取表單中輸入的密碼
String password = (String) authentication.getCredentials;
// 查詢用戶是否存在
SelfUserEntity userInfo = selfUserDetailsService.loadUserByUsername(userName);
if (userInfo == ) {
throw new UsernameNotFoundException("用戶名不存在");
}
// 我們還要判斷密碼是否正確,這裡我們的密碼使用BCryptPasswordEncoder進行加密的
if (!new BCryptPasswordEncoder.matches(password, userInfo.getPassword)) {
throw new BadCredentialsException("密碼不正確");
}
// 還可以加一些其他信息的判斷,比如用戶賬號已停用等判斷
if (userInfo.getStatus.equals("PROHIBIT")){
throw new LockedException("該用戶已被凍結");
}
// 角色集合
Set<grantedauthority> authorities = new HashSet<>;

// 查詢用戶角色
List<sysroleentity> sysRoleEntityList = sysUserService.selectSysRoleByUserId(userInfo.getUserId);
for (SysRoleEntity sysRoleEntity: sysRoleEntityList){
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getRoleName));
}
userInfo.setAuthorities(authorities);
// 進行登錄
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class> authentication) {
return true;
}
}
/<sysroleentity>/<grantedauthority>/<code>

編寫自定義PermissionEvaluator註解驗證

<code>/**
* 自定義權限註解驗證
* @Author Sans
* @CreateTime 2019/10/6 13:31
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysUserService sysUserService;
/**
* hasPermission鑑權方法
* 這裡僅僅判斷PreAuthorize註解中的權限表達式
* 實際中可以根據業務需求設計數據庫通過targetUrl和permission做更復雜鑑權
* @Author Sans
* @CreateTime 2019/10/6 18:25
* @Param authentication 用戶身份
* @Param targetUrl 請求路徑
* @Param permission 請求路徑權限
* @Return boolean 是否通過
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 獲取用戶信息

SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal;
// 查詢用戶權限(這裡可以將權限放入緩存中提升效率)
Set<string> permissions = new HashSet<>;
List<sysmenuentity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId);
for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission);
}
// 權限對比
if (permissions.contains(permission.toString)){
return true;
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
/<sysmenuentity>/<string>/<code>

編寫SpringSecurity核心配置類

<code>/**
* SpringSecurity核心配置類
* @Author Sans
* @CreateTime 2019/10/1 9:40
*/
@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 Sans
* @CreateTime 2019/10/1 14:00
*/
@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 Sans
* @CreateTime 2019/10/1 16:56
* @Param http 請求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests
//不進行權限驗證的請求或資源(從配置文件中讀取)
.antMatchers(JWTConfig.antMatchers.split(",")).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));
}
}
/<code>

五.編寫JWT攔截類

編寫JWT接口請求校驗攔截器

<code>/**
* JWT接口請求校驗攔截器
* 請求接口時會進入這裡驗證Token是否合法和過期
* @Author Sans
* @CreateTime 2019/10/5 16:41
*/
@Slf4j
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 (!=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> authorityMap = JSONObject.parseObject(authority, List.class);
for(Map<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){
log.info("Token過期");
} catch (Exception e) {
log.info("Token無效");
}
}
filterChain.doFilter(request, response);
return;
}
}
/<string>
/<grantedauthority>/<code>

六.權限註解和hasPermission權限擴展

Security允許我們在定義URL方法訪問所應有的註解權限時使用SpringEL表達式,在定義所需的訪問權限時如果對應的表達式返回結果為true則表示擁有對應的權限,反之則沒有權限,會進入到我們配置的UserAuthAccessDeniedHandler(暫無權限處理類)中進行處理.這裡舉一些例子,代碼中註釋有對應的描述.各種註解:SpringBoot 常用註解

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证
<code> /**
* 管理端信息
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<string> 返回數據MAP
*/
@PreAuthorize("hasRole('ADMIN')")
@RequestMapping(value = "/info",method = RequestMethod.GET)
public Map<string> userLogin{
Map<string> result = new HashMap<>;
SelfUserEntity userDetails = SecurityUtil.getUserInfo;
result.put("title","管理端信息");
result.put("data",userDetails);
return ResultUtil.resultSuccess(result);
}
/**
* 擁有ADMIN或者USER角色可以訪問
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<string> 返回數據MAP
*/

@PreAuthorize("hasAnyRole('ADMIN','USER')")
@RequestMapping(value = "/list",method = RequestMethod.GET)
public Map<string> list{
Map<string> result = new HashMap<>;
List<sysuserentity> sysUserEntityList = sysUserService.list;
result.put("title","擁有用戶或者管理員角色都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}
/**
* 擁有ADMIN和USER角色可以訪問
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<string> 返回數據MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasRole('USER')")
@RequestMapping(value = "/menuList",method = RequestMethod.GET)
public Map<string> menuList{
Map<string> result = new HashMap<>;
List<sysmenuentity> sysMenuEntityList = sysMenuService.list;
result.put("title","擁有用戶和管理員角色都可以查看");
result.put("data",sysMenuEntityList);
return ResultUtil.resultSuccess(result);
}
/<sysmenuentity>/<string>/<string>/<string>/<sysuserentity>/<string>/<string>/<string>/<string>/<string>/<string>/<code>

通常情況下使用hasRole和hasAnyRole基本可以滿足大部分鑑權需求,但是有時候面對更復雜的場景上述常規表示式無法完成權限認證,Security也為我們提供瞭解決方案.通過hasPermission來擴展表達式.使用hasPermission首先要實現PermissionEvaluator接口

<code>/**
* 自定義權限註解驗證
* @Author Sans
* @CreateTime 2019/10/6 13:31
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysUserService sysUserService;

/**
* hasPermission鑑權方法
* 這裡僅僅判斷PreAuthorize註解中的權限表達式
* 實際中可以根據業務需求設計數據庫通過targetUrl和permission做更復雜鑑權
* 當然targetUrl不一定是URL可以是數據Id還可以是管理員標識等,這裡根據需求自行設計
* @Author Sans
* @CreateTime 2019/10/6 18:25
* @Param authentication 用戶身份(在使用hasPermission表達式時Authentication參數默認會自動帶上)
* @Param targetUrl 請求路徑
* @Param permission 請求路徑權限
* @Return boolean 是否通過
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 獲取用戶信息
SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal;
// 查詢用戶權限(這裡可以將權限放入緩存中提升效率)
Set<string> permissions = new HashSet<>;
List<sysmenuentity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId);
for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission);
}
// 權限對比
if (permissions.contains(permission.toString)){
return true;
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
/<sysmenuentity>/<string>/<code>

在請求方法上添加hasPermission示例

<code> /**
* 擁有sys:user:info權限可以訪問
* hasPermission 第一個參數是請求路徑 第二個參數是權限表達式
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<string> 返回數據MAP
*/
@PreAuthorize("hasPermission('/admin/userList','sys:user:info')")
@RequestMapping(value = "/userList",method = RequestMethod.GET)
public Map<string> userList{
Map<string> result = new HashMap<>;
List<sysuserentity> sysUserEntityList = sysUserService.list;
result.put("title","擁有sys:user:info權限都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}
/<sysuserentity>/<string>/<string>/<string>/<code>

hasPermission可以也可以和其他表達式聯合使用

<code> /**
* 擁有ADMIN角色和sys:role:info權限可以訪問
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<string> 返回數據MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasPermission('/admin/adminRoleList','sys:role:info')")
@RequestMapping(value = "/adminRoleList",method = RequestMethod.GET)
public Map<string> adminRoleList{
Map<string> result = new HashMap<>;
List<sysroleentity> sysRoleEntityList = sysRoleService.list;
result.put("title","擁有ADMIN角色和sys:role:info權限可以訪問");
result.put("data",sysRoleEntityList);
return ResultUtil.resultSuccess(result);
}
/<sysroleentity>/<string>/<string>/<string>/<code>

七.測試

創建賬戶這裡用戶加密使用了Security推薦的bCryptPasswordEncoder方法

<code> /**
* 註冊用戶
*/
@Test
public void contextLoads {
// 註冊用戶
SysUserEntity sysUserEntity = new SysUserEntity;
sysUserEntity.setUsername("sans");
sysUserEntity.setPassword(bCryptPasswordEncoder.encode("123456"));
// 設置用戶狀態
sysUserEntity.setStatus("NORMAL");
sysUserService.save(sysUserEntity);
// 分配角色 1:ADMIN 2:USER
SysUserRoleEntity sysUserRoleEntity = new SysUserRoleEntity;
sysUserRoleEntity.setRoleId(2L);
sysUserRoleEntity.setUserId(sysUserEntity.getUserId);
sysUserRoleService.save(sysUserRoleEntity);
}
/<code>

登錄USER角色賬號,登錄成功後我們會獲取到身份認證的Token

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

訪問USER角色的接口,把上一步獲取到的Token設置在Headers中,Key為Authorization,我們之前實現的JWTAuthenticationTokenFilter攔截器會根據請求頭中的Authorization獲取並解析Token

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

使用USER角色Token訪問ADMIN角色的接口,會被拒絕,告知未授權(暫無權限會進入我們定義的UserAuthAccessDeniedHandler這個類進行處理)

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

更換ADMIN角色進行登錄並訪問ADMIN接口

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

今天就講到這裡,需要項目地址的可以留言。

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

之前,給大家發過三份Java面試寶典,這次新增了一份,目前總共是四份面試寶典,相信在跳槽前一個月按照面試寶典準備準備,基本沒大問題。

  • 《java面試寶典5.0》(初中級)

  • 《350道Java面試題:整理自100+公司》(中高級)

  • 《資深java面試寶典-視頻版》(資深)

  • 《Java[BAT]面試必備》(資深)

分別適用於初中級,中高級

資深級工程師的面試複習。

內容包含java基礎、javaweb、mysql性能優化、JVM、鎖、百萬併發、消息隊列,高性能緩存、反射、Spring全家桶原理、微服務、Zookeeper、數據結構、限流熔斷降級等等。

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

看到這裡,證明有所收穫


分享到:


相關文章: