您的位置:首页 > 编程语言 > Java开发

(一)、spring boot security 认证--自定义登录实现

2019-01-07 22:19 681 查看
版权声明:本文为博主原创文章,转载请附上博文链接,谢谢! https://blog.csdn.net/qq_30062125/article/details/86031593

Spring Security文章目录

简介

spring security主要分为两部分,认证(authentication)和授权(authority)。

这一篇主要是认证部分,它由 ProviderManager(AuthenticationManager)实现。具体层次结构如下:

认证的核心就是登录,这里简单介绍下security自定义token登录的实现逻辑,同时兼容用户名密码登录。

大体分为以下几个步骤:

  1. 自定义AuthenticationToken实现: 不同登录方式使用不同的token
  2. 自定义AuthenticationProcessingFilter实现:用来过滤指定的登录方式,生成对应的自定义AuthenticationToken实现
  3. 自定义AuthenticationProvider实现:针对不同登录方式提供的认证逻辑
  4. 自定义UserDetailsService实现:自定义用户信息查询服务
  5. WebSecurityConfigurerAdapter声明:security信息配置,将前面的自定义对象注入到流程中。

代码路径

github代码路径

步骤说明

注:仅说明实现方式,逻辑简化处理。

1、自定义AuthenticationProcessingFilter实现

package demo.model;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
*
* @Description:  声明自定义token,是为后面的AuthenticationProvider提供支撑,区分不同类型的处理。
*
* @auther: csp
* @date:  2019/1/7 下午6:25
*
*/
public class LoginToken extends AbstractAuthenticationToken {

private final String token;

public LoginToken(String token) {
super(null);
this.token = token;
}

public LoginToken(String token, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.token = token;
setAuthenticated(true);
}

// 这个地方传递下token,逻辑是简化的逻辑,具体可以根据实际场景处理。
// 如jwt token,解析出来username等信息,放到该token中。
@Override
public Object getCredentials() {
return this.token;
}

@Override
public Object getPrincipal() {
return null;
}
}

2、自定义AuthenticationProcessingFilter实现

package demo.filter;

import demo.model.LoginToken;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @Description: 自定义filter,用来筛选出来想要的登录方式。
*
* @auther: csp
* @date:  2019/1/7 下午6:27
*
*/
public class MyTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

private static final String SPRING_SECURITY_RESTFUL_TOKEN = "token";

public static final String SPRING_SECURITY_RESTFUL_LOGIN_URL = "/tokenLogin";
private boolean postOnly = true;

// 请求路径声明,url不能被权限拦截。
// 会根据AntPathRequestMatcher 筛选请求,符合条件的才会认为有效
public MyTokenAuthenticationFilter() {
super(new AntPathRequestMatcher(SPRING_SECURITY_RESTFUL_LOGIN_URL, null));
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

AbstractAuthenticationToken authRequest;

String token = obtainParameter(request, SPRING_SECURITY_RESTFUL_TOKEN);

authRequest = new LoginToken(token);

// Allow subclasses to set the "details" property
setDetails(request, authRequest);

// 根据AuthenticationManager校验具体的请求,实际的登录验证触发。
return this.getAuthenticationManager().authenticate(authRequest);
}

private void setDetails(HttpServletRequest request,
AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}

private String obtainParameter(HttpServletRequest request, String parameter) {
String result =  request.getParameter(parameter);
return result == null ? "" : result;
}
}

3、自定义AuthenticationProvider实现

package demo.provider;

import demo.model.LoginToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
*
* @Description: token验证逻辑
*
* @auther: csp
* @date:  2019/1/7 下午9:05
*
*/
public class MyTokenProvider implements AuthenticationProvider {

UserDetailsService userDetailsService;

public MyTokenProvider(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (authentication.getCredentials() == null) ? "NONE_PROVIDED"
: (String) authentication.getCredentials();

// loginToken_user
// 这个地方简化处理,实际需要校验token,如jwt token 需要解密 验证信息
if (token.startsWith("loginToken_")) {

// 验证下token对不对,然后加载下信息。
Strin
1d25c
g userName = token.split("_")[1];
UserDetails user = userDetailsService.loadUserByUsername(userName);

LoginToken result = new LoginToken(token, user.getAuthorities());
result.setDetails(authentication.getDetails());

return result;
}

throw new BadCredentialsException("token无效");
}

/**
*
* @Description:  只处理特定类型的登录
*
* @auther: csp
* @date:  2019/1/7 下午9:03
* @param authenticationClass
* @return: boolean
*
*/
@Override
public boolean supports(Class<?> authenticationClass) {
return (LoginToken.class
.isAssignableFrom(authenticationClass));
}

public UserDetailsService getUserDetailsService() {
return userDetailsService;
}

public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}

4、自定义UserDetailsService实现

package demo.service;

import demo.model.UrlGrantedAuthority;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
*
* @Description: 用户信息查询逻辑,这里token认证和用户名登录使用同一个service
*
* @auther: csp
* @date:  2019/1/7 下午9:06
*
*/
@Component public class MyUserDetailsService implements UserDetailsService {

private Logger logger = LoggerFactory.getLogger(getClass());

@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("用户的用户名: {}", username);

List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();

// 模拟下逻辑,简单处理下。
if ("admin".equals(username)) {
// 自定义权限实现
UrlGrantedAuthority authority = new UrlGrantedAuthority(null, "/admin/index");
list.add(authority);
// 封装用户信息,并返回。参数分别是:用户名,密码,用户权限
User user = new User(username, "123456", list);

return user;
}
else if ("user".equals(username)) {
list.add(new SimpleGrantedAuthority("ROLE_USER"));
User user = new User(username, "123456", list);

return user;
}
else {
throw new DisabledException("用户不存在");
}

}
}

5、WebSecurityConfigurerAdapter声明

package demo.config;

import demo.filter.MyTokenAuthenticationFilter;
import demo.provider.MyTokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService myUserDetailsService;

// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {

http
// 将tokenfilter追加进去,筛选出来tokenLogin逻辑。
.addFilterBefore(getTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error").permitAll().and()
.authorizeRequests()
.antMatchers(MyTokenAuthenticationFilter.SPRING_SECURITY_RESTFUL_LOGIN_URL).permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated();
}
// @formatter:on

@Override
public void configure(WebSecurity web) throws Exception {
//忽略请求 不走security filters
web.ignoring().antMatchers("/login-error2","/css/**","/info","/health","/hystrix.stream");
}

/**
* 1、用户验证,指定多个AuthenticationProvider
* 实际执行时候根据provider的supports方法判断是否走逻辑
*
* 2、如果不覆盖,优先会获取AuthenticationProvider bean作为provider;
* 如果没有bean,默认提供DaoAuthenticationProvider
*
* @param auth
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myTokenProvider());
// 未配置时候用户名密码默认登录provider
auth.authenticationProvider(daoAuthenticationProvider());
}

@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider provider1 = new DaoAuthenticationProvider();
// 设置userDetailsService
provider1.setUserDetailsService(myUserDetailsService);
// 禁止隐藏用户未找到异常
provider1.setHideUserNotFoundExceptions(false);
// 使用BCrypt进行密码的hash
//		provider1.setPasswordEncoder(myEncoder());
return provider1;
}

/**
*
* @Description:  自定义token方式认证逻辑provider
*
* @auther: csp
* @date:  2019/1/7 下午9:18
* @return: demo.provider.MyTokenProvider
*
*/
@Bean
public MyTokenProvider myTokenProvider() {
return new MyTokenProvider(myUserDetailsService);
}

//	@Bean
public BCryptPasswordEncoder myEncoder(){
return new BCryptPasswordEncoder(6);
}

/**
* token登录过滤器,用来筛选出来token登录方式。
*/
@Bean
public MyTokenAuthenticationFilter getTokenAuthenticationFilter() {
MyTokenAuthenticationFilter filter = new MyTokenAuthenticationFilter();
try {
// 使用的是默认的authenticationManager
filter.setAuthenticationManager(this.authenticationManagerBean());
} catch (Exception e) {
e.printStackTrace();
}
//		filter.setAuthenticationSuccessHandler(new MyLoginAuthSuccessHandler());
filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/"));
filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login-error2"));
return filter;
}

}

6、验证

  1. 用户名密码登录:

http://127.0.0.1:9999/

admin 123456

user 123456

  1. token登录:

user登录:
http://127.0.0.1:9999/tokenLogin?token=loginToken_user

admin登录:
http://127.0.0.1:9999/tokenLogin?token=loginToken_admin

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: