SpringSecurity入门(四)

18、权限管理/授权

18.1、针对url配置

  • 配置SecurityConfig
package com.wanqi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import java.util.Collections;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Beanpublic UserDetailsService inMemoryUsers(PasswordEncoder encoder) {// The builder will ensure the passwords are encoded before saving in memoryUserDetails user = User.withUsername("user").password(encoder.encode("123")).roles("USER").build();UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("ADMIN").build();UserDetails qifeng = User.withUsername("qifeng").password(encoder.encode("123")).authorities("READ_HELLO","ROLE_ADMIN","ROLE_USER").build();return new InMemoryUserDetailsManager(user, admin, qifeng);}@BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {httpSecurity.authorizeRequests()//权限READ_HELLO.mvcMatchers("/hello").hasAuthority("READ_HELLO")//角色.mvcMatchers("/admin").hasRole("ADMIN").mvcMatchers("/user").hasRole("USER").anyRequest().authenticated().and().csrf().disable().formLogin().and().logout(logout -> logout.logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET"))).logoutSuccessUrl("/login")).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()));return httpSecurity.build();}}
  • 编码HelloController
package com.wanqi.controller;import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/6* @Author wandaren*/@RestController
public class HelloController {@RequestMapping("/hello")public String hello(){return "hello";}@RequestMapping("/admin")public String admin(){return "admin";}@RequestMapping("/user")public String user(){return "user";}
}

18.2、针对方法配置

  • 基于注解EnableMethodSecurity
  • [@EnableMethodSecurity(prePostEnabled ](/EnableMethodSecurity(prePostEnabled ) = true)

image.png

  • 配置开启注解
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
  • 编码测试接口
package com.wanqi.controller;import com.wanqi.pojo.DataDamo;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/6* @Author wandaren*/
@RestController
@RequestMapping("/hi")
public class MethodController {@PreAuthorize("hasRole('ADMIN') and authentication.name=='qifeng'")@RequestMappingpublic String hi() {return "hi";}@PreAuthorize("authentication.name==#name")@RequestMapping("h1")public String hello(String name) {return "hello: " + name;}//filterTarget必须是:数组/集合@PreFilter(value = "filterObject.id%2 != 0", filterTarget = "dataDamos")@RequestMapping("users")public String users(@RequestBody List<DataDamo> dataDamos) {return "hello: " + dataDamos;}@PostAuthorize("returnObject.id==1")@RequestMapping("userId")public DataDamo userId(Integer id) {return new DataDamo(id, "ssss");}@PostFilter("filterObject.id>5")@RequestMapping("lists")public List<DataDamo> lists() {List<DataDamo> list = new ArrayList<>();for (int i = 0; i < 10; i++) {list.add(new DataDamo(i, "sss" + i));}return list;}}

19、基于数据库权限验证

19.1、表结构

菜单/权限角色(可访问)
/admin/**ROLE_ADMIN
/user/**ROLE_USER
/guest/**ROLE_GUEST
用户角色
adminADMIN、USER
userUSER
qifengGUEST

image.png

19.2、sql

  • 菜单/权限
CREATE TABLE `menu` (`id` bigint NOT NULL AUTO_INCREMENT,`pattern` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO menu (pattern) VALUES('/admin/**'),('/user/**'),('/guest/**');
  • 角色
CREATE TABLE `role` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,`name_zh` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO `role` (name,name_zh) VALUES('ROLE_ADMIN','管理员'),('ROLE_USER','普通用户'),('ROLE_GUEST','游客');
  • 菜单/权限-角色关系
CREATE TABLE `menu_role` (`id` bigint NOT NULL AUTO_INCREMENT,`mid` bigint NOT NULL,`rid` bigint NOT NULL,PRIMARY KEY (`id`),KEY `menu_role_mid_IDX` (`mid`,`rid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO `security`.menu_role (mid,rid) VALUES(1,1),(2,2),(3,2),(3,3);
  • 用户
CREATE TABLE `user` (`id` bigint NOT NULL AUTO_INCREMENT,`username` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',`password` varchar(500) COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',`accountNonExpired` tinyint(1) NOT NULL COMMENT '账户是否过期',`enabled` tinyint(1) NOT NULL COMMENT '账户是否激活',`accountNonLocked` tinyint(1) NOT NULL COMMENT '账户是否被锁定',`credentialsNonExpired` tinyint(1) NOT NULL COMMENT '密码是否过期',PRIMARY KEY (`id`),KEY `user_username_IDX` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO `user` (username,password,accountNonExpired,enabled,accountNonLocked,credentialsNonExpired) VALUES('admin','{noop}123',1,1,1,1),('user','{noop}123',1,1,1,1),('qifeng','{noop}123',1,1,1,1);
  • 用户-角色关系
CREATE TABLE `user_role` (`id` bigint NOT NULL AUTO_INCREMENT,`uid` bigint NOT NULL COMMENT '用户编号',`rid` bigint NOT NULL COMMENT '角色编号',PRIMARY KEY (`id`),KEY `user_role_userId_IDX` (`uid`,`rid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO `security`.user_role (uid,rid) VALUES(1,1),(1,2),(2,2),(3,3);

19.3、依赖,数据库配置

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.11</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>
server:port: 8081
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.156.139:3306/security?allowPublicKeyRetrieval=trueusername: wqpassword: qifeng
mybatis:type-aliases-package: com.wanqi.pojomapper-locations: classpath:mapper/*.xml

19.4、实体类与mapper

  • 菜单
package com.wanqi.pojo;import java.util.ArrayList;
import java.util.List;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/7* @Author wandaren*/
public class Menu {private Long id;private String pattern;private List<Role> roles = new ArrayList<>();public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getPattern() {return pattern;}public void setPattern(String pattern) {this.pattern = pattern;}public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}public Menu(String pattern) {this.pattern = pattern;}public Menu() {}@Overridepublic String toString() {return "Menu{" +"id=" + id +", pattern='" + pattern + '\'' +", roles=" + roles +'}';}
}
package com.wanqi.mapper;import com.wanqi.pojo.Menu;
import org.apache.ibatis.annotations.Mapper;import java.util.List;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/7* @Author wandaren*/
@Mapper
public interface MenuMapper {public List<Menu> getAllMenu();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wanqi.mapper.MenuMapper"><resultMap id="MenuResultMap" type="com.wanqi.pojo.Menu"><id property="id" column="id"/><result property="pattern" column="pattern"/><collection property="roles" ofType="com.wanqi.pojo.Role"><id property="id" column="rid"/><result property="name" column="rname"/><result property="nameZh" column="rnameZh"/></collection></resultMap><select id="getAllMenu" resultMap="MenuResultMap">Select m.*, r.id as rid, r.name as rname, r.name_zh as rnameZh From menu mLeft join menu_role mr on m.`id` = mr.`mid`Left join role r on r.`id` = mr.`rid`</select></mapper>
  • 角色
package com.wanqi.pojo;public class Role {private Long id;private String name;private String nameZh;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getNameZh() {return nameZh;}public void setNameZh(String nameZh) {this.nameZh = nameZh;}public Role() {}public Role(String name, String nameZh) {this.name = name;this.nameZh = nameZh;}@Overridepublic String toString() {return "Role{" +"id=" + id +", name='" + name + '\'' +", nameZh='" + nameZh + '\'' +'}';}
}
package com.wanqi.mapper;import com.wanqi.pojo.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper
public interface RoleMapper {/*** 根据用户编号查询角色信息*/List<Role> getRoleByUserId(@Param("userId") Long userId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wanqi.mapper.RoleMapper"><select id="getRoleByUserId" parameterType="Long" resultType="com.wanqi.pojo.Role">select r.id,r.name,r.name_zh as nameZhfrom role r,user_role urwhere r.id = ur.ridand  ur.uid= #{userId}</select></mapper>
  • 用户
package com.wanqi.pojo;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.*;public class User implements UserDetails {private Long id;private String username;private String password;/** 账户是否过期* 在MySQL中,0被认为是false,非零值被认为是true* */private Boolean accountNonExpired = true;/** 账户是否激活 */private Boolean enabled = true;/** 账户是否被锁定 */private Boolean accountNonLocked = true;/** 密码是否过期 */private Boolean credentialsNonExpired = true;private List<Role> roles = new ArrayList<>();public User() {}public User(String username, String password) {this.username = username;this.password = password;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Set<SimpleGrantedAuthority> authorities = new HashSet<>();roles.forEach(role -> authorities.add(new SimpleGrantedAuthority(role.getName())));return authorities;}public Long getId() {return id;}public void setRoles(List<Role> roles) {this.roles = roles;}public List<Role> getRoles() {return roles;}@Overridepublic boolean isAccountNonExpired() {return accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsNonExpired;}@Overridepublic boolean isEnabled() {return enabled;}@Overridepublic String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@Overridepublic String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public void setId(Long id) {this.id = id;}public void setAccountNonExpired(Boolean accountNonExpired) {this.accountNonExpired = accountNonExpired;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public void setAccountNonLocked(Boolean accountNonLocked) {this.accountNonLocked = accountNonLocked;}public void setCredentialsNonExpired(Boolean credentialsNonExpired) {this.credentialsNonExpired = credentialsNonExpired;}
}
package com.wanqi.mapper;import com.wanqi.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface UserMapper {/*** 根据用户名查询用户*/User loadUserByUsername(@Param("username") String username);/*** 添加用户* @param user* @return int*/int save(User user);/**** 更新密码* @param password* @param username* @return int*/int updatePassword(@Param("password")String password,@Param("username") String username);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wanqi.mapper.UserMapper"><select id="loadUserByUsername" parameterType="String" resultType="user">select * from user where username=#{username}</select><insert id="save" parameterType="user">INSERT INTO `user` (username, password, accountNonExpired, enabled, accountNonLocked, credentialsNonExpired)VALUES (#{username}, #{password}, #{accountNonExpired}, #{enabled}, #{accountNonLocked},#{credentialsNonExpired});</insert><update id="updatePassword">UPDATE `user`SET password= #{password}WHERE username = #{username};</update></mapper>

19.5、自定义的资源(url)权限(角色)数据获取类

package com.wanqi.security.filter;import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanqi.mapper.MenuMapper;
import com.wanqi.pojo.Menu;
import com.wanqi.pojo.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;import java.util.Collection;
import java.util.List;/*** @Description 自定义的资源(url)权限(角色)数据获取类* @Version 1.0.0* @Date 2022/9/7* @Author wandaren*/
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {@Autowiredprivate MenuMapper menuMapper;@Beanprivate AntPathMatcher antPathMatcher() {return new AntPathMatcher();}/*** 获取用户请求的某个具体的资源(url)所需要的权限(角色)集合*/@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {//获取当前请求对象String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();//查询所有的菜单List<Menu> allMenu = menuMapper.getAllMenu();System.out.println(JSONUtil.toJsonStr(allMenu));for (Menu menu : allMenu) {if (antPathMatcher().match(menu.getPattern(), requestURI)) {String[] roles = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);System.out.println(JSONUtil.toJsonStr(roles));return SecurityConfig.createList(roles);}}return null;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}
}

19.6、自定义获取账号信息,与密码自动更新

package com.wanqi.service.impl;import cn.hutool.json.JSONUtil;
import com.wanqi.mapper.RoleMapper;
import com.wanqi.mapper.UserMapper;
import com.wanqi.pojo.Role;
import com.wanqi.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;import java.util.List;@Component("userDetailsImpl")
public class UserDetailsImpl implements UserDetailsService, UserDetailsPasswordService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleMapper roleMapper;@Overridepublic org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userMapper.loadUserByUsername(username);System.out.println("---"+JSONUtil.toJsonStr(user));if(user == null){throw new UsernameNotFoundException("用户名不存在");}List<Role> roles = roleMapper.getRoleByUserId(user.getId());System.out.println("---"+JSONUtil.toJsonStr(roles));user.setRoles(roles);return user;}@Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {int state = userMapper.updatePassword(newPassword, user.getUsername());if (state == 1) {((User) user).setPassword(newPassword);}return user;}
}

19.7、自定义RememberMeServices实现类

package com.wanqi.service;import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.servlet.http.HttpServletRequest;/*** @Description 自定义RememberMeServices实现类* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/public class RememberMeServices extends PersistentTokenBasedRememberMeServices {public RememberMeServices(String key, UserDetailsService userDetailsService, PersistentTokenRepository tokenRepository) {super(key, userDetailsService, tokenRepository);}@Overrideprotected boolean rememberMeRequested(HttpServletRequest request, String parameter) {Object attribute = request.getAttribute(AbstractRememberMeServices.DEFAULT_PARAMETER);if (attribute == null) {return false;}String paramValue = attribute.toString();return paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1");}
}

19.8、Security配置类

package com.wanqi.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanqi.security.filter.CustomFilterInvocationSecurityMetadataSource;
import com.wanqi.service.RememberMeServices;
import com.wanqi.service.impl.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configurers.UrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@AutowiredDataSource dataSource;@AutowiredCustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;UserDetailsImpl userDetailsImpl;@Autowiredpublic void setUserDetailsImpl(UserDetailsImpl userDetailsImpl) {this.userDetailsImpl = userDetailsImpl;}@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Autowiredpublic AuthenticationConfiguration authenticationConfiguration;/*** 获取AuthenticationManager(认证管理器),登录时认证使用** @return* @throws Exception*/@Beanpublic AuthenticationManager authenticationManager() throws Exception {return authenticationConfiguration.getAuthenticationManager();}@Beanpublic PersistentTokenRepository jdbcTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//自动创建表结构,首次启动设置为true
//        jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;}@Beanpublic RememberMeServices rememberMeServices(){return new RememberMeServices(UUID.randomUUID().toString(), userDetailsImpl, jdbcTokenRepository());}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//获取工厂对象ApplicationContext applicationContext = httpSecurity.getSharedObject(ApplicationContext.class);//设置自定义url权限处理httpSecurity.apply(new UrlAuthorizationConfigurer<>(applicationContext)).withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O object) {object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);//是否拒绝公共资源访问object.setRejectPublicInvocations(false);return object;}});httpSecurity.formLogin().and().logout(logout -> {logout.logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET"),new AntPathRequestMatcher("/bb", "POST")))//前后端分离时代自定义注销登录处理器.logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "注销成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}})//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(userDetailsImpl).rememberMe().rememberMeServices(rememberMeServices()).tokenRepository(jdbcTokenRepository()).and()//禁止csrf跨站请求保护.csrf().disable();return httpSecurity.build();}}

20、OAuth2

20.1、基于gitee实现快速登陆

  • 依赖
 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>
  • yaml配置
spring:security:oauth2:client:registration:gitee:client-id: daf0946aa26c28a661bbfb5bdb89357f8b90e121b53d98ba8b383afd348904e0client-secret: 1021637c412d22bd2b706f15c0c5c9dad6df859d9f4a01e36b93575b50d98c5cauthorization-grant-type: authorization_coderedirect-uri: http://localhost:8080/login/oauth2/code/giteeclient-name: giteeprovider:gitee:authorization-uri: https://gitee.com/oauth/authorizetoken-uri: https://gitee.com/oauth/tokenuser-info-uri: https://gitee.com/api/v5/useruser-name-attribute: name
  • security配置类
package com.wanqi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.web.SecurityFilterChain;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login();return http.build();}@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.giteeClientRegistration());}private ClientRegistration giteeClientRegistration() {return ClientRegistration.withRegistrationId("gitee").clientId("daf0946aa26c28a661bbfb5bdb89357f8b90e121b53d98ba8b383afd348904e0").clientSecret("1021637c412d22bd2b706f15c0c5c9dad6df859d9f4a01e36b93575b50d98c5c").authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).redirectUri("http://localhost:8080/login/oauth2/code/gitee").authorizationUri("https://gitee.com/oauth/authorize").tokenUri("https://gitee.com/oauth/token").userInfoUri("https://gitee.com/api/v5/user").userNameAttributeName("name").clientName("gitee").build();}
}
 (1)client_id、client-secret替换为Gitee获取的数据(2)authorization-grant-type:授权模式使用授权码模式(3)redirect-uri:回调地址,填写的与Gitee上申请的一致(4)client-name:客户端名称,可以在登录选择页面上显示Gitee的OAuth登录需要自定义provider,Spring Security OAuth提供了配置的方式来实现。(5)authorization-uri:授权服务器地址(6)token-uri:授权服务器获取token地址(7)user-info-uri:授权服务器获取用户信息的地址(8)user-name-attribute:用户信息中的用户名属性
  • gitee创建第三方应用

image.png

20.2、基于内存搭建授权服务器

  • 引入依赖,版本使用2.2.5.RELEASE
 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>
  • 配置security
package com.wanqi.config;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
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.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Bean(value = "bcryptPasswordEncoder")public PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Beanpublic UserDetailsService inMemoryUsers(@Qualifier("bcryptPasswordEncoder") PasswordEncoder encoder) {UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("USER", "ADMIN").build();return new InMemoryUserDetailsManager(admin);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//开启权限验证http.authorizeRequests()//permitAll直接放行,必须在anyRequest().authenticated()前面.mvcMatchers("/toLogin").permitAll().mvcMatchers("/index").permitAll()//anyRequest所有请求都需要认证.anyRequest().authenticated().and()//使用form表单验证.formLogin().and()//注销.logout(logout -> {logout//指定默认注销url,默认请求方式GET//.logoutUrl("/logout").logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET")))//注销成功后跳转页面//.logoutSuccessUrl("/toLogin")//前后端分离时代自定义注销登录处理器.logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "注销成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}})//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()))//禁止csrf跨站请求保护.csrf().disable();}}
  • 自定义 授权服务器配置
package com.wanqi.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;import javax.annotation.Resource;/*** @Description 自定义 授权服务器配置* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Resourceprivate PasswordEncoder bcryptPasswordEncoder;/*** 用来配置授权服务器可以为那些客户端授权* @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("app")//注册客户端密钥.secret(bcryptPasswordEncoder.encode("secret")).redirectUris("https://cn.bing.com")//授权码模式,5选一.authorizedGrantTypes("authorization_code")//.authorizedGrantTypes("client_credentials", "password", "implicit", "authorization_code", "refresh_token");//令牌容许获取的资源权限.scopes("read:user");}
}
请求是否同意授权:http://127.0.0.1:8080/oauth/authorize?client_id=app&redirect_uri=https://cn.bing.com&response_type=code
获取令牌:http://app:secret@localhost:8080/oauth/token
  • 获取令牌

image.png

  • 刷新令牌

image.png

  • 修改自定义 授权服务器配置
.authorizedGrantTypes("authorization_code","refresh_token")@Override
publicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints)throwsException{
endpoints.userDetailsService(userDetailsService);
}
package com.wanqi.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;import javax.annotation.Resource;/*** @Description 自定义 授权服务器配置* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Resourceprivate PasswordEncoder bcryptPasswordEncoder;@Resourceprivate UserDetailsService userDetailsService;/*** 用来配置授权服务器可以为那些客户端授权* @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("app")//注册客户端密钥.secret(bcryptPasswordEncoder.encode("secret")).redirectUris("https://cn.bing.com")/* 授权码模式:client_credentials*  刷新令牌:refresh_token*  */.authorizedGrantTypes("authorization_code","refresh_token")//.authorizedGrantTypes("client_credentials", "password", "implicit", "authorization_code", "refresh_token");//令牌容许获取的资源权限.scopes("read:user");}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.userDetailsService(userDetailsService);}
}

20.3、基于redis搭建授权服务器

1、引入依赖,spirng-boot版本2.2.5.RELEASE

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>

2、配置redis

spring:redis:port: 6379host: 172.16.156.139password: qifengdatabase: 1 #指定数据库

3、Security配置类

package com.wanqi.config;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Bean(value = "bcryptPasswordEncoder")public PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(inMemoryUsers(bcryptPasswordEncoder()));}@Override@Beanprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}@Beanpublic UserDetailsService inMemoryUsers(@Qualifier("bcryptPasswordEncoder") PasswordEncoder encoder) {UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("USER", "ADMIN").build();return new InMemoryUserDetailsManager(admin);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//开启权限验证http.authorizeRequests()//permitAll直接放行,必须在anyRequest().authenticated()前面.mvcMatchers("/toLogin").permitAll().mvcMatchers("/index").permitAll()//anyRequest所有请求都需要认证.anyRequest().authenticated().and()//使用form表单验证.formLogin().and()//注销.logout(logout -> {logout//指定默认注销url,默认请求方式GET//.logoutUrl("/logout").logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET")))//注销成功后跳转页面//.logoutSuccessUrl("/toLogin")//前后端分离时代自定义注销登录处理器.logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "注销成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}})//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()))//禁止csrf跨站请求保护.csrf().disable();}}

4、自定义 授权服务器配置

package com.wanqi.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.annotation.Resource;/*** @Description 自定义 授权服务器配置* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Resourceprivate PasswordEncoder bcryptPasswordEncoder;@Resourceprivate UserDetailsService userDetailsService;@Autowiredprivate AuthenticationManager authenticationManager ;@Autowiredprivate RedisConnectionFactory redisConnectionFactory ;/*** 用来配置授权服务器可以为那些客户端授权** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("app")//注册客户端密钥.secret(bcryptPasswordEncoder.encode("secret")).redirectUris("https://cn.bing.com")/* 授权码模式:client_credentials* 简化模式:implicit* 密码模式:password* 客户端模式:client_credentials*  刷新令牌:refresh_token*  */.authorizedGrantTypes("authorization_code", "refresh_token")//.authorizedGrantTypes("client_credentials", "password", "implicit", "authorization_code", "refresh_token");//令牌容许获取的资源权限.scopes("read:user")// token的有效期.accessTokenValiditySeconds(24*3600)// refresh_token的有效期.refreshTokenValiditySeconds(24*7*3600);super.configure(clients);}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.userDetailsService(userDetailsService).authenticationManager(authenticationManager).tokenStore(redisTokenStore());}public TokenStore redisTokenStore(){return new RedisTokenStore(redisConnectionFactory) ;}/** 请求是否同意授权:http://127.0.0.1:8080/oauth/authorize?client_id=app&redirect_uri=https://cn.bing.com&response_type=code* 获取令牌:http://app:secret@localhost:8080/oauth/token**/
}

20.4、基于redis搭建资源服务器

1、导入依赖

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-resource-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>

2、redis配置

spring:redis:port: 6379host: 172.16.156.139password: qifengdatabase: 1 #指定数据库
server:port: 8081

3、自定义资源服务器配置

package com.wanqi.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;/*** @Description 自定义资源服务器配置* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {@Autowiredprivate RedisConnectionFactory redisConnectionFactory ;@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(redisTokenStore());super.configure(resources);}public TokenStore redisTokenStore(){return new RedisTokenStore(redisConnectionFactory) ;}}

4、模拟资源

package com.wanqi.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@RestController
public class HelloController {@RequestMapping("/hello")public String hello(){return "hello";}
}
  • http://127.0.0.1:8081/hello?access_token=cfa1e9c4-9501-466a-9b87-9ba415bd0821

image.png

20.5、基于jwt搭建授权服务器

1、依赖导入,版本2.2.5.RELEASE

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>

2、Security配置

package com.wanqi.config;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Bean(value = "bcryptPasswordEncoder")public PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(inMemoryUsers(bcryptPasswordEncoder()));}@Override@Beanprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}@Beanpublic UserDetailsService inMemoryUsers(@Qualifier("bcryptPasswordEncoder") PasswordEncoder encoder) {UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("USER", "ADMIN").build();return new InMemoryUserDetailsManager(admin);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//开启权限验证http.authorizeRequests()//permitAll直接放行,必须在anyRequest().authenticated()前面//anyRequest所有请求都需要认证.anyRequest().authenticated().and()//使用form表单验证.formLogin().and()//注销.logout(logout -> {logout//指定默认注销url,默认请求方式GET//.logoutUrl("/logout").logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET")))//注销成功后跳转页面//.logoutSuccessUrl("/toLogin")//前后端分离时代自定义注销登录处理器.logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "注销成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}})//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()))//禁止csrf跨站请求保护.csrf().disable();}}

3、jwt内容增强

package com.wanqi.config;import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import sun.jvm.hotspot.opto.HaltNode;import java.util.HashMap;
import java.util.Map;/*** @Description jwt内容增强* @Version 1.0.0* @Date 2022/9/9* @Author wandaren*/
public class JwtTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {Map<String,Object> map = new HashMap<>();map.put("test", "jwt内容增强");((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(map);return accessToken;}
}

4、授权服务器配置

package com.wanqi.config;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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.util.ArrayList;
import java.util.List;@Configuration
// 开启授权服务器的功能
@EnableAuthorizationServer
public class JWTAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsService userDetailsService;/*** 添加第三方的客户端*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()// 第三方客户端的名称.withClient("app")//  第三方客户端的密钥.secret(passwordEncoder.encode("secret")).redirectUris("https://cn.bing.com")/* 授权码模式:client_credentials* 简化模式:implicit* 密码模式:password* 客户端模式:client_credentials*  刷新令牌:refresh_token*  */.authorizedGrantTypes("authorization_code", "refresh_token")//第三方客户端的授权范围.scopes("all")// token的有效期.accessTokenValiditySeconds(24 * 3600)// refresh_token的有效期.refreshTokenValiditySeconds(24 * 7 * 3600);super.configure(clients);}/*** 配置验证管理器,UserdetailService*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {super.configure(endpoints);//配置jwt增强内容TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();List<TokenEnhancer> list = new ArrayList<>();list.add(jwtTokenEnhancer());list.add(jwtAccessTokenConverter());tokenEnhancerChain.setTokenEnhancers(list);endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService)//设置token 存储策略.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()).tokenEnhancer(tokenEnhancerChain);}/*** jwtTokenStore** @return*/@Beanpublic TokenStore jwtTokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();tokenConverter.setSigningKey("name");return tokenConverter;}@BeanJwtTokenEnhancer jwtTokenEnhancer() {return new JwtTokenEnhancer();}
}

20.6、基于jwt搭建资源服务器

1、依赖导入,版本2.2.5.RELEASE

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-resource-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>

2、资源服务器配置

package com.wanqi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;/*** @Description 自定义资源服务器配置* @Version 1.0.0* @Date 2022/9/8* @Author wandaren*/
@Configuration
@EnableResourceServer
public class JWTResourceServerConfigurer extends ResourceServerConfigurerAdapter {@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {super.configure(resources);resources.resourceId("app").tokenStore(jwtTokenStore()).stateless(true);}@Beanpublic TokenStore jwtTokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();tokenConverter.setSigningKey("name");return tokenConverter;}}

image.png

  • 解析jwt令牌https://jwt.io/

image.png

  • 请求资源服务器
  • http://127.0.0.1:8081/hello?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjI3NzU0MjMsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2MDhmYWIwOS1jN2NkLTQyOWItYjVhMy0yZmM3YzI1NDU3ZmQiLCJjbGllbnRfaWQiOiJhcHAiLCJzY29wZSI6WyJhbGwiXX0.NpwTkYDmtrNZRNFG5WIvxZqH9FBXhPHSojcCi7GiKfA
  • 请求头使用Authorization:Bearer XXXXXX或者使用参数access_token=XXXXXXX

image.png

相关文章

SpringSecurity入门(一)

SpringSecurity入门(二)

SpringSecurity入门(三)

完结撒花

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/346738.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(免费领源码)基于 node.js#vue#mysql的网上游戏商城35112-计算机毕业设计项目选题推荐

摘 要 本论文主要论述了如何使用node.js语言开发一个基于vue的网上游戏商城&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;本系统采用的数据库是Mysql&#xff0c;使用node.js的koa技术技术构建的一个管理系统&#xff0c;实现了本系统的全部功能。在…

云计算-期末复习题-选择/判断/填空/简答(1)

目录 填空题/简答题 单选题 多选题 判断题 云计算期末复习部分练习题&#xff0c;下一章会补全。祝大家好好复习&#xff0c;顺利通过课程。 填空题/简答题 >保障云基本安全的对策包括&#xff08;&#xff09;、&#xff08;&#xff09;和&#xff08;&#xff09; &…

Linux:桌面系统中的文件后缀和类型

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 Linux中的文件后缀与Windows系统有些不同&#xff0c;因为其似乎没有很重要&#xff0c;一个文件是否可执行对后缀没有要求。但是&#xff0c;后缀依然可以用于表示文件…

机器学习笔记:label smoothing

在传统的分类任务中&#xff0c;我们通常使用硬标签&#xff08;hard labels&#xff09; 即如果一个样本属于某个类别&#xff0c;其对应的标签就是一个全0的向量&#xff0c;除了表示这个类别的位置为1。例如&#xff0c;在一个3类分类任务中&#xff0c;某个样本的标签可能是…

实用软件分享---简单菜谱 0.3版本 几千种美食(安卓)

专栏介绍:本专栏主要分享一些实用的软件(Po Jie版); 声明1:软件不保证时效性;只能保证在写本文时,该软件是可用的;不保证后续时间该软件能一直正常运行;不保证没有bug;如果软件不可用了,我知道后会第一时间在题目上注明(已失效)。介意者请勿订阅。 声明2:本专栏的…

【Python】 装饰器,可不只是装饰作用!

Python 是一种高级编程语言&#xff0c;以其清晰的语法和代码可读性而著称。在 Python 中&#xff0c;“at” 符号&#xff08;&#xff09;通常被称为装饰器&#xff08;Decorator&#xff09;的语法符号。装饰器是一种设计模式&#xff0c;用于修改或增强函数、方法或类的行为…

Spring Cloud Gateway详解

一、前言Spring Cloud Gateway的作用 路由转发&#xff1a; Spring Cloud Gateway作为微服务架构中的网关服务&#xff0c;充当所有请求的入口。它可以根据请求的路径、Host、Header、请求参数等多种条件进行路由&#xff0c;将请求转发到相应的微服务实例。路由信息由ID、目的…

2024蓝桥杯初赛决赛pwn题全解

蓝桥杯初赛决赛pwn题解 初赛第一题第二题 决赛getting_startedbabyheap 初赛 第一题 有system函数&#xff0c;并且能在bss上读入字符 而且存在栈溢出&#xff0c;只要过掉check函数即可 check函数中&#xff0c;主要是对system常规获取权限的参数&#xff0c;进行了过滤&…

git版本控制工具常用命令

一、本地仓库管理 push 向远程推送代码 pulll 拉取代码 二、远程仓库管理 三、分支操作 本地主分支master 远程主分支main head指向当前分支 查看&#xff1a;git branch 创建分支: git branch 名字 切换分支&#xff1a;git checkout 名字 合并分支&#xff1a;git…

VS2019创建c++动态链接库dll与调用方法

VS2019创建c动态链接库dll与调用方法 1.点击文件-》新建-》项目&#xff0c;输入dll,选择具有导出项的(DLL)动态链接库 2.输入一个文件名&#xff1a;dll2 头文件.h 3.添加加减法函数&#xff1a; // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的…

爱普生SMD3225贴片晶振升级版TSX-3225

爱普生有一款外形尺寸3.2*2.5mm的无源贴片晶振&#xff0c;型号TSX-3225&#xff0c;也是非常直观的能从型号分辨其封装尺寸大小的&#xff0c;被广泛应用于便携式的无线传输设备&#xff0c;同时&#xff0c;这也是一款非常成熟的产品&#xff0c;毕竟SMD3225封装是目前市场主…

部署LVS-DR模式(附带详细实验)

目录 一.数据包流向分析 二.DR模式特点 三.ARP问题及解决办法 四.实验部署 1.配置负载调度器&#xff08;192.168.80.105&#xff09; 1.1.安装并启用ipvsadm 1.2.配置虚拟IP地址&#xff08;VIP&#xff1a;192.168.80.100&#xff09; 1.3.调整 proc 响应参数 1.4.配…

springboot 在线心理咨询管理系统-计算机毕业设计源码82552

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设在线心理咨询管理系统…

Elasticsearch index 设置 false,为什么还可以被检索到?

在 Elasticsearch 中&#xff0c;mapping 定义了索引中的字段类型及其处理方式。 近期有球友提问&#xff0c;为什么设置了 index: false 的字段仍能被检索。 本文将详细探讨这个问题&#xff0c;并引入列式存储的概念&#xff0c;帮助大家更好地理解 Elasticsearch 的存储和查…

IO模型和多路转接

叠甲&#xff1a;以下文章主要是依靠我的实际编码学习中总结出来的经验之谈&#xff0c;求逻辑自洽&#xff0c;不能百分百保证正确&#xff0c;有错误、未定义、不合适的内容请尽情指出&#xff01; 文章目录 1.IO 概要1.1.IO 低效原因1.2.IO 常见模型1.2.1.阻塞 IO1.2.2.非阻…

shell脚本简单命令

shell脚本 脚本就是可运行代码的集合&#xff0c;脚本语言&#xff08;计算机语言&#xff09;脚本的特点&#xff1a;从上到下&#xff0c;按行执行。 python 脚本语言 格式更严谨 严格的执行锁进。也是从上到下按行执行。 shell脚本就是在shell环境&#xff08;/bin/bash&…

Word忘记保存?请使用Word隐藏备份文件

大家用Word写材料时&#xff0c;如果忘记保存&#xff0c;可以使用Word隐藏备份文件找回未保存的文件。&#xff08;仅供参考&#xff09; Windows7、8、10、11系统的设置如下&#xff1a; 执行上述操作&#xff0c;可以在word文件菜单中信息项的自动保存中找到了。上述内容…

Qt for Android 申请摄像头权限

步骤 1. 添加用户权限 方式1: AndroidManifest.xml 中新增&#xff08;不添加后面申请选项时不弹窗&#xff09; 或者再Qt Creator中直接添加 方式2: .pro 中引用multimedia 模块&#xff0c;编译时配置自动添加 <uses-permission android:name"android.permissi…

我给KTV服务生讲解防抖,他竟然听懂了

端午节三天假期&#xff0c;的最后一天&#xff0c;我和朋友闲来无事&#xff0c;想着去唱会儿歌吧&#xff0c;好久不唱了&#xff0c;于是吃了午饭&#xff0c;石景山就近找了一家KTV&#xff0c;我们团好了卷就过去了。 装修还算不错&#xff0c;很快找到服务生&#xff0c…

Day 43 keepalived高可用集群

keepalived高可用集群 负载均衡 lb集群 load balance ​ 流量分发 高可用 ha集群 high availability ​ 主要是给服务器做冗余 keepalive 持久连接 保持存活 keepalived 高可用软件名称 红帽有自己的高可用集群套件&#xff1a;RHCS keepalived介绍 ​ keepalived是集…