深入淺析springsecurity入門登錄授權(quán)
①我們需要自定義登陸接口,也就是在controller目錄新建LoginController類,在controller方法里面去調(diào)用service接口,在service接口實(shí)現(xiàn)AuthenticationManager去進(jìn)行用戶的認(rèn)證,注意,我們定義的controller方法要讓SpringSecurity對這個接口放行(如果不放行的話,會被SpringSecurity攔截),讓用戶訪問這個接口的時候不用登錄也能訪問。
②在service接口中我們通過AuthenticationManager的authenticate方法來進(jìn)行用戶認(rèn)證,所以需要在SecurityConfig中配置把AuthenticationManager注入容器
③認(rèn)證成功的話要生成一個jwt,放入響應(yīng)中返回。并且為了讓用戶下回請求時能通過jwt識別出具體的是哪個用戶,我們需要把用戶信息存入redis,可以把用戶id作為key。
JwtAuthenticationTokenFilter過濾器類
package com.sangeng.filter;
import com.sangeng.domain.LoginUser;
import com.sangeng.utils.JwtUtil;
import com.sangeng.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
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.Objects;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private RedisCache redisCache;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//獲取token
String token = request.getHeader("token");
if (!StringUtils.hasText(token)) {
//放行
filterChain.doFilter(request, response);
return;
}
//解析token
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("token非法");
}
//從redis中獲取用戶信息
String redisKey = "login:" + userid;
LoginUser loginUser = redisCache.getCacheObject(redisKey);
if(Objects.isNull(loginUser)){
throw new RuntimeException("用戶未登錄");
}
//存入SecurityContextHolder
//TODO 獲取權(quán)限信息封裝到Authentication中
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//放行
filterChain.doFilter(request, response);
}
}在 LoginController類添加如下
package com.sangeng.controller;
import com.sangeng.domain.ResponseResult;
import com.sangeng.domain.User;
import com.sangeng.service.LoginServcie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@Autowired
private LoginServcie loginServcie;
@PostMapping("/user/login")
public ResponseResult login(@RequestBody User user){
//登錄
return loginServcie.login(user);
}
}在service目錄新建 LoginService 接口
package com.huanf.service;
import com.huanf.domain.ResponseResult;
import com.huanf.domain.User;
import org.springframework.stereotype.Service;
/**
* @author 35238
* @date 2023/7/12 0012 11:45
*/
@Service
public interface LoginService {
ResponseResult login(User user);
}LoginUser類
package com.sangeng.domain;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {
private User user;
private List<String> permissions;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
private List<SimpleGrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if(authorities!=null){
return authorities;
}
authorities = permissions.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}在LoginServiceImpl類添加如下
package com.sangeng.service.impl;
import com.sangeng.domain.LoginUser;
import com.sangeng.domain.ResponseResult;
import com.sangeng.domain.User;
import com.sangeng.service.LoginServcie;
import com.sangeng.utils.JwtUtil;
import com.sangeng.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Service
public class LoginServiceImpl implements LoginServcie {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private RedisCache redisCache;
@Override
public ResponseResult login(User user) {
//AuthenticationManager authenticate進(jìn)行用戶認(rèn)證
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//如果認(rèn)證沒通過,給出對應(yīng)的提示
if(Objects.isNull(authenticate)){
throw new RuntimeException("登錄失敗");
}
//如果認(rèn)證通過了,使用userid生成一個jwt jwt存入ResponseResult返回
LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
String userid = loginUser.getUser().getId().toString();
String jwt = JwtUtil.createJWT(userid);
Map<String,String> map = new HashMap<>();
map.put("token",jwt);
//把完整的用戶信息存入redis userid作為key
redisCache.setCacheObject("login:"+userid,loginUser);
return new ResponseResult(200,"登錄成功",map);
}
}UserDetailsServiceImpl implements UserDetailsService
package com.sangeng.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sangeng.domain.LoginUser;
import com.sangeng.domain.User;
import com.sangeng.mapper.MenuMapper;
import com.sangeng.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查詢用戶信息
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
User user = userMapper.selectOne(queryWrapper);
//如果沒有查詢到用戶就拋出異常
if(Objects.isNull(user)){
throw new RuntimeException("用戶名或者密錯誤");
}
// List<String> list = new ArrayList<>(Arrays.asList("system:dept:list","admin"));
List<String> list = menuMapper.selectPermsByUserId(user.getId());
//把數(shù)據(jù)封裝成UserDetails返回
return new LoginUser(user,list);
}
}配置類SecurityConfig
package com.sangeng.config;
import com.sangeng.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//創(chuàng)建BCryptPasswordEncoder注入容器
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//關(guān)閉csrf
.csrf().disable()
//不通過Session獲取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 對于登錄接口 允許匿名訪問
.antMatchers("/user/login").anonymous()
// .antMatchers("/testCors").hasAuthority("system:dept:list222")
// 除上面外的所有請求全部需要鑒權(quán)認(rèn)證
.anyRequest().authenticated();
//添加過濾器
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}MenuMapper接口
package com.sangeng.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sangeng.domain.Menu;
import java.util.List;
public interface MenuMapper extends BaseMapper<Menu> {
List<String> selectPermsByUserId(Long userid);
}MenuMapper.xml
<?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.sangeng.mapper.MenuMapper">
<select id="selectPermsByUserId" resultType="java.lang.String">
SELECT
DISTINCT m.`perms`
FROM
sys_user_role ur
LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
WHERE
user_id = #{userid}
AND r.`status` = 0
AND m.`status` = 0
</select>
</mapper>第一步: 在你數(shù)據(jù)庫的security 庫,新建 sys_menu權(quán)限表、sys_role角色表、sys_role_menu中間表、sys_user_role中間表,并插入數(shù)據(jù)
create database if not exists security;
use security;
CREATE TABLE `sys_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`menu_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '菜單名',
`path` varchar(200) DEFAULT NULL COMMENT '路由地址',
`component` varchar(255) DEFAULT NULL COMMENT '組件路徑',
`visible` char(1) DEFAULT '0' COMMENT '菜單狀態(tài)(0顯示 1隱藏)',
`status` char(1) DEFAULT '0' COMMENT '菜單狀態(tài)(0正常 1停用)',
`perms` varchar(100) DEFAULT NULL COMMENT '權(quán)限標(biāo)識',
`icon` varchar(100) DEFAULT '#' COMMENT '菜單圖標(biāo)',
`create_by` bigint(20) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_by` bigint(20) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`del_flag` int(11) DEFAULT '0' COMMENT '是否刪除(0未刪除 1已刪除)',
`remark` varchar(500) DEFAULT NULL COMMENT '備注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='權(quán)限表';
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(128) DEFAULT NULL,
`role_key` varchar(100) DEFAULT NULL COMMENT '角色權(quán)限字符串',
`status` char(1) DEFAULT '0' COMMENT '角色狀態(tài)(0正常 1停用)',
`del_flag` int(1) DEFAULT '0' COMMENT 'del_flag',
`create_by` bigint(200) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_by` bigint(200) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`remark` varchar(500) DEFAULT NULL COMMENT '備注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
CREATE TABLE `sys_role_menu` (
`role_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '角色I(xiàn)D',
`menu_id` bigint(200) NOT NULL DEFAULT '0' COMMENT '菜單id',
PRIMARY KEY (`role_id`,`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `sys_user_role` (
`user_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`role_id` bigint(200) NOT NULL DEFAULT '0' COMMENT '角色id',
PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into sys_user_role values (2,1);
insert into sys_role values
(1,'經(jīng)理','ceo',0,0,default,default,default,default,default),
(2,'程序員','coder',0,0,default,default,default,default,default);
insert into sys_role_menu values (1,1),(1,2);
insert into sys_menu values
(1,'部門管理','dept','system/dept/index',0,0,'system:dept:list','#',default,default,default,default,default,default),
(2,'測試','test','system/test/index',0,0,'system:test:list','#',default,default,default,default,default,default)
create database if not exists security;
use security;
CREATE TABLE `sys_user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`user_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '用戶名',
`nick_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '昵稱',
`password` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '碼',
`status` CHAR(1) DEFAULT '0' COMMENT '賬號狀態(tài)(0正常 1停用)',
`email` VARCHAR(64) DEFAULT NULL COMMENT '郵箱',
`phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手機(jī)號',
`sex` CHAR(1) DEFAULT NULL COMMENT '用戶性別(0男,1女,2未知)',
`avatar` VARCHAR(128) DEFAULT NULL COMMENT '頭像',
`user_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '用戶類型(0管理員,1普通用戶)',
`create_by` BIGINT(20) DEFAULT NULL COMMENT '創(chuàng)建人的用戶id',
`create_time` DATETIME DEFAULT NULL COMMENT '創(chuàng)建時間',
`update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人',
`update_time` DATETIME DEFAULT NULL COMMENT '更新時間',
`del_flag` INT(11) DEFAULT '0' COMMENT '刪除標(biāo)志(0代表未刪除,1代表已刪除)',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';
insert into sys_user values (1,'admin','管理員','{noop}123456','0',DEFAULT,DEFAULT,DEFAULT,DEFAULT,'0',DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT);
insert into sys_user values (2,'huanf','渙沷a靑惷','$2a$10$YPnG.IYUk0mMechaxSibBuKmNeTzvuHdcxkqvoxizsll6WCQG9CHG','0',DEFAULT,DEFAULT,DEFAULT,DEFAULT,'1',DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT);授權(quán)的基本流程
在SpringSecurity中,會使用默認(rèn)的FilterSecurityInterceptor來進(jìn)行權(quán)限校驗(yàn)。在FilterSecurityInterceptor中會從SecurityContextHolder獲取其中的Authentication,然后獲取其中的權(quán)限信息。當(dāng)前用戶是否擁有訪問當(dāng)前資源所需的權(quán)限

所以我們在項(xiàng)目中只需要把當(dāng)前登錄用戶的權(quán)限信息也存入Authentication,然后設(shè)置我們的資源所需要的權(quán)限即可
自定義訪問路徑的權(quán)限
SpringSecurity為我們提供了基于注解的權(quán)限控制方案,這也是我們項(xiàng)目中主要采用的方式。我們可以使用注解去指定訪問對應(yīng)的資源所需的權(quán)限
第一步: 在SecurityConfig配置類添加如下,作用是開啟相關(guān)配置
@EnableGlobalMethodSecurity(prePostEnabled = true)
HelloController類
package com.sangeng.controller;
import com.sangeng.domain.ResponseResult;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
@PreAuthorize("@ex.hasAuthority('system:dept:list222')")
// @PreAuthorize("hasAnyAuthority('admin','test','system:dept:list')")
// @PreAuthorize("hasRole('system:dept:list')")
// @PreAuthorize("hasAnyRole('admin','sy stem:dept:list')")
public ResponseResult hello(){
return new ResponseResult(200,"hello");
}
}SGExpressionRoot.class 自定義權(quán)限類
package com.sangeng.expression;
import com.sangeng.domain.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("ex")
public class SGExpressionRoot {
public boolean hasAuthority(String authority){
//獲取當(dāng)前用戶的權(quán)限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
List<String> permissions = loginUser.getPermissions();
//判斷用戶權(quán)限集合中是否存在authority
return permissions.contains(authority);
}
}到此這篇關(guān)于springsecurity入門登錄授權(quán)的文章就介紹到這了,更多相關(guān)springsecurity入門登錄授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程
- SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
- SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動態(tài)權(quán)限問題
- SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)
- SpringSecurity動態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問題
- SpringSecurity實(shí)現(xiàn)權(quán)限認(rèn)證與授權(quán)的使用示例
- SpringSecurity進(jìn)行認(rèn)證與授權(quán)的示例代碼
- springSecurity用戶認(rèn)證和授權(quán)的實(shí)現(xiàn)
- mall整合SpringSecurity及JWT實(shí)現(xiàn)認(rèn)證授權(quán)實(shí)戰(zhàn)
- SpringSecurity頁面授權(quán)與登錄驗(yàn)證實(shí)現(xiàn)(內(nèi)存取值與數(shù)據(jù)庫取值)
- SpringSecurity 鑒權(quán)與授權(quán)的具體使用
相關(guān)文章
java IO流 之 輸入流 InputString()的使用
這篇文章主要介紹了java IO流 之 輸入流 InputString()的使用,以及讀取數(shù)據(jù)的三種方式詳解,非常不錯,需要的朋友可以參考下2016-12-12
Java如何使用逆波蘭式(后綴表達(dá)式)計(jì)算表達(dá)式的值
這篇文章主要介紹了Java如何使用逆波蘭式(后綴表達(dá)式)計(jì)算表達(dá)式的值,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
Java 利用dom方式讀取、創(chuàng)建xml詳解及實(shí)例代碼
這篇文章主要介紹了Java 利用dom方式讀取、創(chuàng)建xml的相關(guān)資料,需要的朋友可以參考下2017-03-03
基于Java中對域和靜態(tài)方法的訪問不具有多態(tài)性(實(shí)例講解)
下面小編就為大家?guī)硪黄贘ava中對域和靜態(tài)方法的訪問不具有多態(tài)性(實(shí)例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Mybatis執(zhí)行SQL時多了一個limit的問題及解決方法
這篇文章主要介紹了Mybatis執(zhí)行SQL時多了一個limit的問題及解決方法,Mybatis攔截器方法識別到配置中參數(shù)supportMethodsArguments 為ture時會分頁處理,本文結(jié)合示例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下2022-10-10

