Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность
Эта статья приняла участие"Проект "Звезда раскопок"", чтобы выиграть творческие подарочные пакеты и бросить вызов творческим поощрениям.
📖Предисловие
心态好了,就没那么累了。心情好了,所见皆是明媚风景。
“一时解决不了的问题,那就利用这个契机,看清自己的局限性,对自己进行一场拨乱反正。”正如老话所说,一念放下,万般自在。如果你正被烦心事扰乱心神,不妨学会断舍离。断掉胡思乱想,社区垃圾情绪,离开负面能量。心态好了,就没那么累了。心情好了,所见皆是明媚风景。
🚓引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--freemarker,页面渲染引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot 监控客户端 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入数据库密码加密 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt.version}</version>
</dependency>
<!-- 引入mysql链接依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector}</version>
<scope>provided</scope>
</dependency>
<!-- 引入Druid依赖
java.sql.SQLFeatureNotSupportedException
http://www.vmfor.com/p/101494868463.html-->
<dependency>
<!--自动配置-->
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${common-pool.version}</version>
</dependency>
<!--<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>-->
<!-- 引入 MybatisPlus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 引入多数据源依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${mybatis-plus-dynamic.version}</version>
</dependency>
</dependencies>
1. Добавьте класс запуска
@EnableFeignClients
//对外开启暴露获取token的API接口
@EnableResourceServer
@EnableDiscoveryClient
2. Создайте профиль авторизацииAuthorizationServerConfig.java
Согласно официальной инструкции, нам нужно создать класс конфигурации для реализации
AuthorizationServerConfigurer
Итак, мы создаем класс, чтобы наследовать его класс реализацииAuthorizationServerConfigurerAdapter
, конкретный код выглядит следующим образом:
package com.cyj.dream.auth.config;
import com.cyj.dream.auth.entity.SysUser;
import com.cyj.dream.auth.persistence.service.impl.CustomUserServiceImpl;
import com.cyj.dream.core.constant.Constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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.redis.RedisTokenStore;
import javax.sql.DataSource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @Description: 授权的配置文件
* @BelongsProject: DreamChardonnay
* @BelongsPackage: com.cyj.dream.auth.config
* @Author: ChenYongJia
* @CreateTime: 2021-09-30
* @Email: chen87647213@163.com
* @Version: 1.0
*/
// 授权认证服务中心配置
@Configuration // 配置类
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private CustomUserServiceImpl userDetailsService;
/**
* accessToken 有效期 2小时
*/
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 7200 * 12 * 7;
/**
* accessToken 有效期 2小时
*/
private static final int REFRESH_TOKEN_VALIDITY_SECONDS = 7200 * 12 * 7;
/**
* 定制化处理
* 配置tokenStore的存储方式是redis存储
* <p>
* 这里存储在数据库,大家可以结合自己的业务场景考虑将access_token存入数据库还是redis
*
* @return
*/
@Bean
public TokenStore redisTokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
// redis key 前缀--DreamCloud
tokenStore.setPrefix(Constant.tokenPrefix + "_");
return tokenStore;
}
/**
* 配置客户端的管理是jdbc
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("dream").secret(passwordEncoder.encode("c83fb51ff6e807e8805c6dd9d5707365"))
// 授权码授权模式下的回调地址
.redirectUris("http://www.baidu.com")
.authorizedGrantTypes("authorization_code", "password", "refresh_token").scopes("all")
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
// 授权类型
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
//clients.withClientDetails(new JdbcClientDetailsService(dataSource));
}
/**
* 认证服务器Endpoints配置
*
* @return
* @author ChenYongJia
* @date 2021/9/30
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.authenticationManager(authenticationManager).allowedTokenEndpointRequestMethods(HttpMethod.GET,
HttpMethod.POST, HttpMethod.PUT,
HttpMethod.DELETE)
.tokenEnhancer(tokenEnhancerChain)
//配置tokenStore管理、配置客户端详情--使用redis
.tokenStore(redisTokenStore()).userDetailsService(userDetailsService)
//配置授权模式
.tokenGranter(tokenGranter(endpoints));
//配置tokenServices的参数 +
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//配置accessToken过期时间
defaultTokenServices.setAccessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(2));
//配置refreshToken的过期时间
defaultTokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30));
//设置支持刷新token
defaultTokenServices.setReuseRefreshToken(true);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(endpoints.getTokenStore());
defaultTokenServices.setClientDetailsService(endpoints.getClientDetailsService());
defaultTokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
endpoints.tokenServices(defaultTokenServices);
}
/**
* 认证服务器相关接口权限管理
*
* @return
* @author ChenYongJia
* @date 2021/9/30
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//配置允许表单访问
security.allowFormAuthenticationForClients()
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("permitAll()");
}
/**
* 配置授权模式也可以添加自定义模式(不写也有默认的)
* 具体可查看AuthorizationServerEndpointsConfigurer中的getDefaultTokenGranters方法
* 以后添加一个手机验证码的功能
*
* @param endpoints
* @return
*/
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> list = new ArrayList<>();
//增加刷新token
list.add(new RefreshTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//授权码模式
list.add(new AuthorizationCodeTokenGranter(endpoints.getTokenServices(), endpoints.getAuthorizationCodeServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//客户端凭证模式
list.add(new ClientCredentialsTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//密码模式
list.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//隐藏式
list.add(new ImplicitTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
return new CompositeTokenGranter(list);
}
/**
* 创建一个token增强方法,增加一些我们自己想要返回的附加信息
*
* @return
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
final Map<String, Object> additionalInfo = new HashMap<>(2);
additionalInfo.put("license", "SunnyChen-DreamChardonnay");
SysUser user = (SysUser) authentication.getUserAuthentication().getPrincipal();
if (user != null) {
additionalInfo.put("userId", user.getSysUserId());
additionalInfo.put("userPhone", user.getSysUserPhone());
additionalInfo.put("userDeptId", user.getSysUserInfoDepartmentId());
}
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
};
}
/**
* 用来做验证
*
* @return
*/
@Bean
AuthenticationManager authenticationManager() {
AuthenticationManager authenticationManager = new AuthenticationManager() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return daoAuhthenticationProvider().authenticate(authentication);
}
};
return authenticationManager;
}
/**
* 用来做验证
*
* @return
*/
@Bean
public AuthenticationProvider daoAuhthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
}
3. Так как сам сервер авторизации тоже является сервером ресурсов, то создается и конфигурация ресурса, код такой
package com.cyj.dream.auth.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* @Description: 资源配置器
* @BelongsProject: DreamChardonnay
* @BelongsPackage: com.cyj.dream.auth.config
* @Author: ChenYongJia
* @CreateTime: 2021-09-30
* @Email: chen87647213@163.com
* @Version: 1.0
*/
@Configuration
// 启用资源服务
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 配置资源接口安全,http.authorizeRequests()针对的所有url,但是由于登录页面url包含在其中,这里配置会进行token校验,校验不通过返回错误json,
* 而授权码模式获取code时需要重定向登录页面,重定向过程并不能携带token,所有不能用http.authorizeRequests(),
* 而是用requestMatchers().antMatchers(""),这里配置的是需要资源接口拦截的url数组
*
* @param http
* @return void
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http //配置需要保护的资源接口
.requestMatchers().
antMatchers("/user", "/test/need_token", "/logout", "/remove", "/update", "/test/need_admin", "/test/scope")
.and().authorizeRequests().anyRequest().authenticated();
}
}
4. СоздатьwebSecurity
настроить
/*
* OK ,关于这个配置我要多说两句:
*
* 1.首先当我们要自定义Spring Security的时候我们需要继承自WebSecurityConfigurerAdapter来完成,相关配置重写对应
* 方法即可。 2.我们在这里注册CustomUserService的Bean,然后通过重写configure方法添加我们自定义的认证方式。
* 3.在configure(HttpSecurity http)方法中,我们设置了登录页面,而且登录页面任何人都可以访问,然后设置了登录失败地址,也设置了注销请求,
* 注销请求也是任何人都可以访问的。
* 4.permitAll表示该请求任何人都可以访问,.anyRequest().authenticated(),表示其他的请求都必须要有权限认证。
* 5.这里我们可以通过匹配器来匹配路径,比如antMatchers方法,假设我要管理员才可以访问admin文件夹下的内容,我可以这样来写:.
* antMatchers("/admin/**").hasRole("ROLE_ADMIN"),也可以设置admin文件夹下的文件可以有多个角色来访问,
* 写法如下:.antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
* 6.可以通过hasIpAddress来指定某一个ip可以访问该资源,假设只允许访问ip为210.210.210.210的请求获取admin下的资源,
* 写法如下.antMatchers("/admin/**").hasIpAddress("210.210.210.210")
* 7.更多的权限控制方式参看源码:
*/
код показывает, как показано ниже:
package com.cyj.dream.auth.config;
import com.cyj.dream.auth.handler.*;
import com.cyj.dream.auth.persistence.service.impl.CustomUserServiceImpl;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @Description: WebSecurity配置
* @BelongsProject: DreamChardonnay
* @BelongsPackage: com.cyj.dream.auth.config
* @Author: ChenYongJia
* @CreateTime: 2021-09-30
* @Email: chen87647213@163.com
* @Version: 1.0
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserServiceImpl userDetailsService;
/**
* 自定义登录成功处理器
*/
@Autowired
private MyAuthenticationSuccessHandler userLoginSuccessHandler;
/**
* 自定义登录失败处理器
*/
@Autowired
private MyAuthenticationFailureHandler userLoginFailureHandler;
/**
* 自定义注销成功处理器
*/
@Autowired
private UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 自定义暂无权限处理器
*/
@Autowired
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
/**
* 自定义未登录的处理器
*/
@Autowired
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public UserDetailsService userDetailsService() {
return new CustomUserServiceImpl();
}
/**
* 配置登录验证逻辑
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
/**
* 认证管理--需要配置这个支持password模式
* <p>
* support password grant type
*
* @return 认证管理对象
* @throws Exception 认证异常信息
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 安全请求配置,这里配置的是Security的部分,请求全部通过,安全拦截在资源服务器配置
* <p>
* http安全配置
*
* @param http http安全对象
* @throws Exception http安全异常信息
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置允许的请求以及跨域问题
http.authorizeRequests()
.antMatchers("/webjars/**", "/js/**", "/css/**", "/images/*", "/fonts/**", "/**/*.png", "/**/*.jpg",
"/static/**")
// .anyRequest()
.permitAll()
// 所有请求都可以访问,本地开发打开下面一行的注释
//.antMatchers("/**").permitAll()
//不进行权限验证的请求或资源(从配置文件中读取),本地开发可以,将本行注释掉
.antMatchers("/login", "/oauth/**")
.permitAll()
.antMatchers("/**")
.fullyAuthenticated()
.and()
//配置未登录自定义处理类
.httpBasic()
.authenticationEntryPoint(userAuthenticationEntryPointHandler)
.and()
.csrf()
.disable()
// 配置登录地址
.formLogin()
// 本地
//.loginPage("/login/userLogin")
//.loginProcessingUrl("/login/userLogin")
.loginPage("/login")
//配置登录成功自定义处理类
.successHandler(userLoginSuccessHandler)
//配置登录失败自定义处理类
.failureHandler(userLoginFailureHandler)
.permitAll()
.and()
//配置登出地址
.logout()
// /userInfo/loginOutByToken
.logoutUrl("/login/loginOut")
//配置用户登出自定义处理类
.logoutSuccessHandler(userLogoutSuccessHandler)
.and()
//配置没有权限自定义处理类
.exceptionHandling()
.accessDeniedHandler(userAuthAccessDeniedHandler)
.and()
// 开启跨域
.cors()
.and()
// 取消跨站请求伪造防护
.csrf().disable();
/*.cors().and()
.addFilterAt(ignoreLogoutFilter, LogoutFilter.class);*/
// 为了可以使用 iframe 内嵌页面加载
http.headers().frameOptions().sameOrigin();
// 基于Token不需要session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存
http.headers().cacheControl();
}
@Override
public void configure(WebSecurity web) throws Exception {
// 设置忽略资源
web.ignoring().antMatchers(
"/error",
"/v2/api-docs/**",
"/favicon.ico",
"/css/**",
"/js/**",
"/images/*",
"/fonts/**",
"/**/*.png",
"/**/*.jpg")
// 不拦截 swagger2 所进行的配置
.antMatchers("/templates/**")
.antMatchers("/static/**")
.antMatchers("/webjars/**")
.antMatchers("/swagger-ui.html/**")
.antMatchers("/v2/**")
.antMatchers("/doc.html")
.antMatchers("/api-docs-ext/**")
.antMatchers("/swagger-resources/**");
;
}
}
5. Создайтеcontroller
Проведите тест доступа
package com.cyj.dream.auth.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cyj.dream.auth.entity.SysUser;
import com.cyj.dream.auth.persistence.service.ITbSysUserService;
import com.cyj.dream.core.aspect.annotation.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
/**
* @Description: 用户控制器
* @BelongsProject: DreamChardonnay
* @BelongsPackage: com.cyj.dream.auth.controller
* @Author: ChenYongJia
* @CreateTime: 2021-09-30 10:40
* @Email: chen87647213@163.com
* @Version: 1.0
*/
@Slf4j
@ResponseResult
@RestController
@RequestMapping(value = "/authUser", name = "用户控制器")
@Api(value = "authUser", tags = "用户控制器")
public class UserController {
@Autowired
private ITbSysUserService iTbSysUserService;
@ApiOperation("通过名称获取用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "userName", value = "用户名称", dataType = "String", required = true)
})
@RequestMapping(value = "getByName", method = RequestMethod.GET, name = "通过名称获取用户信息")
public SysUser getByName(@RequestParam(value = "userName") String userName){
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, userName);
return iTbSysUserService.getOne(wrapper);
}
@ApiOperation("获取授权的用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "principal", value = "当前用户", dataType = "Principal", required = true)
})
@RequestMapping(value = "current", method = RequestMethod.GET, name = "获取授权的用户信息")
public Principal user(Principal principal){
// 授权信息
return principal;
}
}
пояс
token
запрос
Без
token
запрос
пояс
token
, но ни одинROLE_ADMIN
разрешение
6. Напоследок оFegin
вызов службыToken
потерянный вопрос
В микросервисах мы часто используем RestTemplate или Fegin для вызовов между сервисами, здесь будет проблема: при вызове других сервисов токен будет потерян, в результате чего у нас не будет доступа.
Итак, нам нужно добавить несколько перехватчиков в нашtoken
забрать, дляfegin
Код конфигурации выглядит следующим образом:
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
// 设置请求头
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
requestTemplate.header(name, value);
}
}
// 设置请求体,这里主要是为了传递 access_token
Enumeration<String> parameterNames = request.getParameterNames();
StringBuilder body = new StringBuilder();
if (parameterNames != null) {
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
String value = request.getParameter(name);
// 将 Token 加入请求头
if ("access_token".equals(name)) {
requestTemplate.header("authorization", "Bearer " + value);
}
// 其它参数加入请求体
else {
body.append(name).append("=").append(value).append("&");
}
}
}
// 设置请求体
if (body.length() > 0) {
// 去掉最后一位 & 符号
body.deleteCharAt(body.length() - 1);
requestTemplate.body(body.toString());
}
}
}
Затем добавьте этот перехватчик в нашfegin
В перехватчике запросов:
@Configuration
public class FeignRequestConfiguration {
@Bean
public RequestInterceptor requestInterceptor() {
return new FeignRequestInterceptor();
}
}
7. Spring Security
а такжеShiro
Тот же пункт:
1: функция аутентификации
2: Функция авторизации
3: функция шифрования
4: Управление сеансом
5: поддержка кэша
6: функция «запомнить меня»...
разница:
преимущество:
1: Spring Security разработан на основе Spring.Если Spring используется в качестве основы в проекте, удобнее сотрудничать со Spring Security для выполнения разрешений, а Shiro необходимо интегрировать со Spring.
2: Spring Security имеет больше функций, чем Shiro, например, защита безопасности
3: Ресурсы сообщества Spring Security богаче, чем Широ
недостаток:
1: Настройка и использование Shiro относительно просты, а Spring Security сложен для начала.
2: Shiro имеет мало зависимостей, не требует фреймворка и контейнера и может работать независимо, в то время как Spring Security зависит от контейнера Spring.
8. Таблицы базы данных
-- used in tests that use HSQL
create table oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
create table oauth_client_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
create table oauth_access_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication LONGVARBINARY,
refresh_token VARCHAR(256)
);
create table oauth_refresh_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication LONGVARBINARY
);
create table oauth_code (
code VARCHAR(256), authentication LONGVARBINARY
);
create table oauth_approvals (
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
-- customized oauth_client_details table
create table ClientDetails (
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);
Наконец, спасибо за ваше терпение и просмотр. Для конкретных запросов и некоторых результатов, пожалуйста, обратитесь к предыдущей главе.
🎉Резюме:
- Дополнительные справочные сообщения в блоге см. здесь:"Блог Чен Юнцзя"
- Интегрировать
auth
Вот некоторые классы, которые я не разместил, вы можете сначала прочитать статью, а я передам кодgit
Легко учиться по вашей ссылке ~ - Друзья, которым нравятся блогеры, могут подписаться, поставить лайк и продолжать обновлять, хе-хе!