需求:其他项目要跳转我们的springboot+springsecurity项目,需要授权特定token登陆。
实现思路:添加过滤器,拦截请求中的token,传递给Authentication鉴权,如果这个请求不带有授权信息,被拦截后,会跳转登录页面。
一.先来了解下springsecurity,熟悉的跳过该部分
1)Spring Security介绍:
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。它的核心是一组过滤器链,不同的功能经由不同的过滤器。要实现的功能就是在认证服务器上登录,然后获取Token,再访问资源服务器中的资源。核心功能:认证和授权。
2)Spring Security 认证流程:
3)Spring Security 项目搭建:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
4)访问页面:
导入spring-boot-starter-security启动器后,Spring Security已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面。
在浏览器输入:http://localhost:8080/ 进入Spring Security内置登录页面
用户名:user。
密码:项目启动,打印在控制台中。
5)自定义用户名和密码:
修改application.yml文件
# 静态用户,一般只在内部网络认证中使用,如:内部服务器1,访问服务器2
spring:
security:
user:
name: test # 通过配置文件,设置静态用户名
password: test # 配置文件,设置静态登录密码
6)UserDetailsService详解:
什么也没有配置的时候,账号和密码是由Spring Security定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过 「自定义逻辑控制认证逻辑」 。如果需要自定义逻辑时,只需要实现UserDetailsService接口。
@Component
public class UserSecurity implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userService.login(userName);
System.out.println(user);
if (null==user){
throw new UsernameNotFoundException("用户名错误");
}
org.springframework.security.core.userdetails.User result =
new org.springframework.security.core.userdetails.User(
userName,user.getPassword(), AuthorityUtils.createAuthorityList()
);
return result;
}
}
7)PasswordEncoder密码解析器详解
「PasswordEncoder」 是SpringSecurity 的密码解析器,用户密码校验、加密 。自定义登录逻辑时要求必须给容器注入PaswordEncoder的bean对象,SpringSecurity 定义了很多实现接口 「PasswordEncoder」 满足我们密码加密、密码校验 使用需求PasswordEncoder密码解析器详解
自定义密码解析器:
/**
* 凭证匹配器,用于做认证流程的凭证校验使用的类型
* 其中有2个核心方法
* 1. encode - 把明文密码,加密成密文密码
* 2. matches - 校验明文和密文是否匹配
* */
public class MyMD5PasswordEncoder implements PasswordEncoder {
/**
* 加密
* @param charSequence 明文字符串
* @return
*/
@Override
public String encode(CharSequence charSequence) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
return toHexString(digest.digest(charSequence.toString().getBytes()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
/**
* 密码校验
* @param charSequence 明文,页面收集密码
* @param s 密文 ,数据库中存放密码
* @return
*/
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(encode(charSequence));
}
/**
* @param tmp 转16进制字节数组
* @return 返回16进制字符串
*/
private String toHexString(byte [] tmp){
StringBuilder builder = new StringBuilder();
for (byte b :tmp){
String s = Integer.toHexString(b & 0xFF);
if (s.length()==1){
builder.append("0");
}
builder.append(s);
}
return builder.toString();
}
}
/**
* 加密
* @return 加密对象
* 如需使用自定义密码凭证匹配器 返回自定义加密对象
* 例如: return new MD5PasswordEncoder();
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //Spring Security 自带
}
8)登录配置
方式一 转发
http.formLogin()
.usernameParameter("name") // 设置请求参数中,用户名参数名称。 默认username
.passwordParameter("pswd") // 设置请求参数中,密码参数名称。 默认password
.loginPage("/toLogin") // 当用户未登录的时候,跳转的登录页面地址是什么? 默认 /login
.loginProcessingUrl("/login") // 用户登录逻辑请求地址是什么。 默认是 /login
.failureForwardUrl("/failure"); // 登录失败后,请求转发的位置。Security请求转发使用Post请求。默认转发到:loginPage?error
.successForwardUrl("/toMain"); // 用户登录成功后,请求转发到的位置。Security请求转发使用POST请求。
方式二 :重定向
http.formLogin()
.usernameParameter("name") // 设置请求参数中,用户名参数名称。 默认username
.passwordParameter("pswd") // 设置请求参数中,密码参数名称。 默认password
.loginPage("/toLogin") // 当用户未登录的时候,跳转的登录页面地址是什么? 默认 /login
.loginProcessingUrl("/login") // 用户登录逻辑请求地址是什么。 默认是 /login
.defaultSuccessUrl("/toMain",true); //用户登录成功后,响应重定向到的位置。GET请求。必须配置绝对地址。
.failureUrl("/failure"); // 登录失败后,重定向的位置。
方式三:自定义登录处理器
/*自定义登录失败处理器*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String url;
private boolean isRedirect;
public MyAuthenticationFailureHandler(String url, boolean isRedirect) {
this.url = url;
this.isRedirect = isRedirect;
}
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
if (isRedirect){
httpServletResponse.sendRedirect(url);
}else {
httpServletRequest.getRequestDispatcher(url).forward(httpServletRequest,httpServletResponse);
}
}
//get set 方法 省略
自定义登录成功逻辑处理器
/**
* 自定义登录成功后处理器
* 转发重定向,有代码逻辑实现
* */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private String url;
private boolean isRedirect;
public MyAuthenticationSuccessHandler(String url, boolean isRedirect) {
this.url = url;
this.isRedirect = isRedirect;
}
/** * @param request 请求对象 request.getRequestDispatcher.forward() * @param response 响应对象 response.sendRedirect() * @param authentication 用户认证成功后的对象。其中报换用户名权限结合,内容是 * 自定义UserDetailsService * */
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if (isRedirect){
response.sendRedirect(url);
}else {
request.getRequestDispatcher(url).forward(request,response);
}
}
//get set 方法 省略
登录相关配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserSecurity userSecurity;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
/**
* 加密
* @return 加密对象
* 如需使用自定义加密逻辑 返回自定义加密对象
* return new MD5PasswordEncoder(); return new SimplePasswordEncoder();
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //Spring Security 自带
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 配置登录请求相关内容。
http.formLogin()
.loginPage("/toLogin") // 当用户未登录的时候,跳转的登录页面地址是什么? 默认 /login
.usernameParameter("name") // 设置请求参数中,用户名参数名称。 默认username
.passwordParameter("pswd") // 设置请求参数中,密码参数名称。 默认password
.loginProcessingUrl("/login") //设置登录 提交表单数据访问请求地址
.defaultSuccessUrl("/toMain")
.failureUrl("/toLogin");
//.successForwardUrl("/toMain")
//.failureForwardUrl("/toLogin");
//.successHandler(new LoginSuccessHandler("/toMain", true)) //自定义登录成功处理器
//.failureHandler(new LoginErrorHandler("/toLogin", true));
http.authorizeRequests()
//.antMatchers("/toLogin").anonymous() //只能匿名用户访问
.antMatchers("/toLogin", "/register", "/login", "/favicon.ico").permitAll() // /toLogin请求地址,可以随便访问。
.antMatchers("/**/*.js").permitAll() // 授予所有目录下的所有.js文件可访问权限
.regexMatchers(".*[.]css").permitAll() // 授予所有目录下的所有.css文件可访问权限
.anyRequest().authenticated(); // 任意的请求,都必须认证后才能访问。
// 配置退出登录
http.logout()
.invalidateHttpSession(true) // 回收HttpSession对象。退出之前调用HttpSession.invalidate() 默认 true
.clearAuthentication(true) // 退出之前,清空Security记录的用户登录标记。 默认 true
// .addLogoutHandler() // 增加退出处理器。
.logoutSuccessUrl("/") // 配置退出后,进入的请求地址。 默认是loginPage?logout
.logoutUrl("/logout"); // 配置退出登录的路径地址。和页面请求地址一致即可。
// 关闭CSRF安全协议。
// 关闭是为了保证完整流程的可用。
http.csrf().disable();
}
@Bean
public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
}
角色权限
「hasAuthority(String)」 判断角色是否具有特定权限
http.authorizeRequests().antMatchers("/main1.html").hasAuthority("admin")
「hasAnyAuthority(String …)」 如果用户具备给定权限中某一个,就允许访问
http.authorizeRequests().antMatchers("/admin/read").hasAnyAuthority("xxx","xxx")
「hasRole(String)」 如果用户具备给定角色就允许访问。否则出现403
//请求地址为/admin/read的请求,必须登录用户拥有'管理员'角色才可访问
http.authorizeRequests().antMatchers("/admin/read").hasRole("管理员")
「hasAnyRole(String …)」 如果用户具备给定角色的任意一个,就允许被访问
//用户拥有角色是管理员 或 访客 可以访问 /guest/read
http.authorizeRequests().antMatchers("/guest/read").hasAnyRole("管理员", "访客")
「hasIpAddress(String)」 请求是指定的IP就运行访问
//ip 是127.0.0.1 的请求 可以访问/ip
http.authorizeRequests().antMatchers("/ip").hasIpAddress("127.0.0.1")
9)403 权限不足页面处理
编写类实现接口
/** * @describe 403 权限不足 * @author: AnyWhere */
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(
"<html>" +
"<body>" +
"<div style='width:800px;text-align:center;margin:auto;font-size:24px'>" +
"权限不足,请联系管理员" +
"</div>" +
"</body>" +
"</html>"
);
response.getWriter().flush();//刷新缓冲区
}
}
配置类中配置exceptionHandling
// 配置403访问错误处理器。
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);/
10)RememberMe(记住我)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置记住密码
http.rememberMe()
.rememberMeParameter("remember-me") // 修改请求参数名。 默认是remember-me
.tokenValiditySeconds(14*24*60*60) // 设置记住我有效时间。单位是秒。默认是14天
.rememberMeCookieName("remember-me") // 修改remember me的cookie名称。默认是remember-me
.tokenRepository(persistentTokenRepository) // 配置用户登录标记的持久化工具对象。
.userDetailsService(userSecurity); // 配置自定义的UserDetailsService接口实现类对象
}
@Bean
public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
}
二.来了解下springsecurity的用户名密码登录实现,熟悉的跳过该部分
1.配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${http.login.fail.url}")
private String loginFailUrl;
@Value("${http.logout.success.url}")
private String logoutSuccessUrl;
@Value("${http.white.url.active}")
private String whiteActiveUrl;
@Autowired
private Environment env;
@Autowired
public void configGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthenticationProvider()).eraseCredentials(true);
}
//用户名和密码登陆处理
@Bean
public CustomAuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
/**
* 添加用户名和密码登陆验证的过滤器
*/
@Bean
public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
String[] whiteActiveUrls = whiteActiveUrl.split(",");
List<String> whiteUrls = new ArrayList<String>();
for (int i = 0; i < whiteActiveUrls.length; i++) {
String item = whiteActiveUrls[i];
item = env.getProperty("http.white.urls." + item);
if (StringUtils.isEmpty(item)) {
continue;
}
whiteUrls.add(item);
}
whiteUrls.add("/static/**");
// 白名单
http.authorizeRequests().antMatchers(whiteUrls.toArray(new String[whiteUrls.size()])).permitAll().requestMatchers(CorsUtils::isPreFlightRequest);
http.headers().frameOptions().disable();
// 定义登陆成功之后的页面跳转
http.formLogin().loginPage("/login").defaultSuccessUrl("/index").successForwardUrl("/index");
//添加用户名密码过滤器。
http.addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// 登出相关url
http.logout().logoutSuccessUrl(logoutSuccessUrl);
// 要求对request都进行是否授权的验证
http.authorizeRequests().anyRequest().authenticated();
}
}
2.定义一个AuthenticationToken,他的实例包含了用户登陆时的信息,封装后用于校验。
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = -1076492615339314113L;
public CustomAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public CustomAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
3.过滤器的定义
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public CustomAuthenticationFilter() {
//父类中定义了拦截的请求URL,/login的post请求,直接使用这个配置,也可以自己重写
super();
//添加了自定义的登陆失败处理器,配置文件中直接配置failureUrl没能直接生效
super.setAuthenticationFailureHandler(new LoginFailureHandler());
}
/**
* 这里主要是把 request中的用户名和密码参数取出来,封装CustomAuthenticationToken,然后getAuthenticationManager().authenticate(authRequest)进行校验
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!request.getMethod().equals(HttpMethod.POST.name())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request).trim();
String password = obtainPassword(request).trim();
final int userNameLen = 32;
if (username.length() == 0 || username.length() > userNameLen || password.length() == 0
|| password.length() > userNameLen) {
throw new BadCredentialsException("username or password is wrong!");
}
CustomAuthenticationToken authRequest = new CustomAuthenticationToken(username, password);
setDetails(request, authRequest);
Authentication authentication = getAuthenticationManager().authenticate(authRequest);
return authentication;
}
@Override
protected String obtainUsername(HttpServletRequest request) {
String username = super.obtainUsername(request);
return username == null ? "" : username;
}
@Override
protected String obtainPassword(HttpServletRequest request) {
String password = super.obtainPassword(request);
return password == null ? "" : password;
}
}
4.具体的校验逻辑定义
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserServiceFeignClient userServiceFeignClient;
// 根用户拥有全部的权限
private final List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("CAN_SEARCH"), new SimpleGrantedAuthority("CAN_EXPORT"), new SimpleGrantedAuthority("CAN_IMPORT"), new SimpleGrantedAuthority("CAN_BORROW"), new SimpleGrantedAuthority("CAN_RETURN"), new SimpleGrantedAuthority("CAN_REPAIR"), new SimpleGrantedAuthority("CAN_DISCARD"), new SimpleGrantedAuthority("CAN_EMPOWERMENT"), new SimpleGrantedAuthority("CAN_BREED"));
//这里通过用户名和密码的检查匹配判断登陆是否成功。
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.isAuthenticated()) {
return authentication;
}
String username = authentication.getName();
User user = null;
try {
user = userServiceFeignClient.getUserByUsername(username);
} catch (BusinessException e) {
throw new BadCredentialsException("该用户不存在,用户名: " + username);
}
if (user == null) {
throw new BadCredentialsException("该用户不存在,用户名: " + username);
}
String password;
try {
password = encode((String)authentication.getCredentials());
} catch (NoSuchAlgorithmException e) {
throw new BadCredentialsException("密码错误");
}
if (!password.equals(user.getPassword())) {
throw new BadCredentialsException("密码错误");
}
//这个是自定义用户登陆信息
SecurityUser securityUser = new SecurityUser();
securityUser.setUsername(user.getUsername());
securityUser.setPassword(user.getPassword());
securityUser.setUid(user.getUid());
return new CustomAuthenticationToken(securityUser, authentication.getCredentials(), authorities);
}
public static String encode(String str) throws NoSuchAlgorithmException{
MessageDigest instance = MessageDigest.getInstance("MD5");
byte[] digest = instance.digest(str.getBytes());
StringBuffer sb = new StringBuffer();
for (byte b : digest) {
int j = b & 0xff;
String hexString = Integer.toHexString(j);
if (hexString.length() < 2) {
hexString = "0" + hexString;
}
sb.append(hexString);
}
return sb.toString();
}
//这里定义provider是否被调用,需要执行结果为true才会执行验证逻辑
@Override
public boolean supports(Class<?> authentication) {
return CustomAuthenticationToken.class.isAssignableFrom(authentication);
}
}
这个就是用户名和密码登陆校验的逻辑。而token登陆的逻辑基本一致,只是校验token即可,不校验用户名密码。
三.token实现跳转实现参考代码:
1.用户名密码登录配置中添加token跳转配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${http.login.fail.url}")
private String loginFailUrl;
@Value("${http.logout.success.url}")
private String logoutSuccessUrl;
@Value("${http.white.url.active}")
private String whiteActiveUrl;
@Autowired
private Environment env;
/**
* 此处给AuthenticationManager添加登陆验证的逻辑。
* 这里添加了两个AuthenticationProvider分别用于用户名密码登陆的验证以及token授权登陆两种方式。
* 在处理登陆信息的过滤器执行的时候会调用这两个provider进行登陆验证。
*/
@Autowired
public void configGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthenticationProvider()).authenticationProvider(tokenAuthenticationProvider()).eraseCredentials(true);
}
//用户名和密码登陆处理
@Bean
public CustomAuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
//token登陆处理
@Bean
public TokenAuthenticationProvider tokenAuthenticationProvider() {
return new TokenAuthenticationProvider();
}
/**
* 添加token登陆验证的过滤器
*/
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() throws Exception {
TokenAuthenticationFilter filter = new TokenAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
/**
* 添加用户名和密码登陆验证的过滤器
*/
@Bean
public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
String[] whiteActiveUrls = whiteActiveUrl.split(",");
List<String> whiteUrls = new ArrayList<String>();
for (int i = 0; i < whiteActiveUrls.length; i++) {
String item = whiteActiveUrls[i];
item = env.getProperty("http.white.urls." + item);
if (StringUtils.isEmpty(item)) {
continue;
}
whiteUrls.add(item);
}
whiteUrls.add("/static/**");
// 白名单
http.authorizeRequests().antMatchers(whiteUrls.toArray(new String[whiteUrls.size()])).permitAll().requestMatchers(CorsUtils::isPreFlightRequest);
http.headers().frameOptions().disable();
// 定义登陆成功之后的页面跳转
http.formLogin().loginPage("/login").defaultSuccessUrl("/index").successForwardUrl("/index");
//分别添加 token 过滤器和 用户名密码过滤器。
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// 登出相关url
http.logout().logoutSuccessUrl(logoutSuccessUrl);
// 要求对request都进行是否授权的验证
http.authorizeRequests().anyRequest().authenticated();
}
}
配置文件定义了两个登陆验证的过滤器TokenAuthenticationFilter和UsernamePasswordAuthenticationFilter;
TokenAuthenticationFilter-验证token登录逻辑;
UsernamePasswordAuthenticationFilter-验证用户名密码登录逻辑;
TokenAuthenticationFilter过滤器会对我们指定的跳转url进行拦截,通过我们定义的验证逻辑判断登陆信息是否正确。如果正确将授权信息添加到服务器。
2.token授权过滤器定义:
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String tokenParameter = "token";
/**
* @param defaultFilterProcessesUrl
*/
public TokenAuthenticationFilter() {
super("/quicklogin");//放行的请求url
super.setAuthenticationFailureHandler(new LoginFailureHandler());//失败页面
}
/**
* {@inheritDoc}
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!request.getMethod().equals(HttpMethod.POST.name())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String token = obtainToken(request);
if (token == null || token.length() == 0) {
throw new BadCredentialsException("uid or token is null.");
}
TokenAuthenticationToken authRequest = new TokenAuthenticationToken(token);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainToken(HttpServletRequest request) {
String token = request.getParameter(this.tokenParameter);
return token == null ? "" : token.trim();
}
}
3.AuthenticationProvider的定义:
public class TokenAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserServiceFeignClient userServiceFeignClient;
// 根用户拥有全部的权限
private final List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("CAN_SEARCH"), new SimpleGrantedAuthority("CAN_EXPORT"), new SimpleGrantedAuthority("CAN_IMPORT"), new SimpleGrantedAuthority("CAN_BORROW"), new SimpleGrantedAuthority("CAN_RETURN"), new SimpleGrantedAuthority("CAN_REPAIR"), new SimpleGrantedAuthority("CAN_DISCARD"), new SimpleGrantedAuthority("CAN_EMPOWERMENT"), new SimpleGrantedAuthority("CAN_BREED"));
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.isAuthenticated()) {
return authentication;
}
//获取过滤器封装的token信息
TokenAuthenticationToken authenticationToken = (TokenAuthenticationToken) authentication;
User user = userServiceFeignClient.getUserTokenByToken((String)authenticationToken.getPrincipal());
// 不通过
if (user == null) {
throw new BadCredentialsException("授权token无效,请重新登陆");
}
SecurityUser securityUser = new SecurityUser();
securityUser.setUsername(user.getUsername());
securityUser.setPassword(user.getPassword());
securityUser.setUid(user.getUid());
TokenAuthenticationToken authenticationResult = new TokenAuthenticationToken(securityUser, authorities);
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return TokenAuthenticationToken.class.isAssignableFrom(authentication);
}
}
4.AuthenticationToken的定义:
public class TokenAuthenticationToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = -6231962326068951783L;
public TokenAuthenticationToken(Object principal) {
super(principal, "");
}
public TokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(principal, "", authorities);
}
}
定义AuthenticationToken,他的实例包含了用户登陆时的信息,封装后用于校验。
5.登陆失败信息的获取:
前文我们自定义了一个登陆失败的handler如下。只是定义了失败后跳转的URL。
@Component
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
public LoginFailureHandler() {
super("/login?error=true");
}
}
前端获取登陆失败的信息
<p th:if="${param.error}" th:text="${session?.SPRING_SECURITY_LAST_EXCEPTION?.message}" ></p>