深入淺析springsecurity入門登錄授權(quán)
①我們需要自定義登陸接口,也就是在controller目錄新建LoginController類,在controller方法里面去調(diào)用service接口,在service接口實現(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)限校驗。在FilterSecurityInterceptor中會從SecurityContextHolder獲取其中的Authentication,然后獲取其中的權(quán)限信息。當(dāng)前用戶是否擁有訪問當(dāng)前資源所需的權(quán)限
所以我們在項目中只需要把當(dāng)前登錄用戶的權(quán)限信息也存入Authentication,然后設(shè)置我們的資源所需要的權(quán)限即可
自定義訪問路徑的權(quán)限
SpringSecurity為我們提供了基于注解的權(quán)限控制方案,這也是我們項目中主要采用的方式。我們可以使用注解去指定訪問對應(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)文章希望大家以后多多支持腳本之家!
- SpringSecurity實現(xiàn)權(quán)限認(rèn)證與授權(quán)的使用示例
- SpringSecurity進(jìn)行認(rèn)證與授權(quán)的示例代碼
- springSecurity用戶認(rèn)證和授權(quán)的實現(xiàn)
- SpringBoot整合SpringSecurity認(rèn)證與授權(quán)
- SpringSecurityOAuth2實現(xiàn)微信授權(quán)登錄
- SpringBoot+SpringSecurity實現(xiàn)基于真實數(shù)據(jù)的授權(quán)認(rèn)證
- springsecurity第三方授權(quán)認(rèn)證的項目實踐
- SpringSecurity數(shù)據(jù)庫進(jìn)行認(rèn)證和授權(quán)的使用
- SpringSecurity授權(quán)機(jī)制的實現(xiàn)(AccessDecisionManager與投票決策)
相關(guān)文章
Java反射機(jī)制詳解_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java反射機(jī)制的相關(guān)資料,主要包括反射的概念、作用2017-06-06