使用spring security框架实现权限以及登录,loadUserByUsername这个方法与authenticate这个方法执行顺序
时间: 2023-01-11 20:55:36 浏览: 260
Spring Security 是一个用于为 Java 应用程序提供安全保护的框架。
在使用 Spring Security 时,当用户尝试登录时,会执行以下步骤:
1. 调用 `authenticate()` 方法进行身份验证,该方法需要用户提供的用户名和密码来尝试进行身份验证。
2. 在进行身份验证之前,Spring Security 会调用 `loadUserByUsername()` 方法来获取用户信息。该方法通常用于在数据库中查询用户信息,然后将其封装在 `UserDetails` 接口的实现类中,并返回给 Spring Security。
3. 当 `loadUserByUsername()` 方法返回用户信息后,Spring Security 会使用该信息与用户提供的密码进行匹配,以确定用户身份是否有效。
因此,在执行过程中,`loadUserByUsername()` 方法会优先于 `authenticate()` 方法执行。
相关问题
springsecurity实现手机验证码登录的方法
Spring Security提供了一个非常灵活的身份验证框架,可以轻松地实现各种类型的身份验证,包括手机验证码登录。下面是实现手机验证码登录的步骤:
1. 配置SecurityConfig
在SecurityConfig类中添加配置,指定手机验证码登录的URL、登录成功和失败的处理逻辑等。
```
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private MyAuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private MyAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.authorizeRequests()
.antMatchers("/sms/code").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
```
2. 自定义身份验证过滤器
创建一个自定义的身份验证过滤器,用于验证用户输入的验证码是否正确。
```
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String mobileParameter = "mobile";
private String smsCodeParameter = "smsCode";
private boolean postOnly = true;
public SmsCodeAuthenticationFilter() {
super(new AntPathRequestMatcher("/login/sms", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String mobile = obtainMobile(request);
String smsCode = obtainSmsCode(request);
if (StringUtils.isBlank(mobile) || StringUtils.isBlank(smsCode)) {
throw new AuthenticationServiceException("Mobile and SmsCode must not be empty");
}
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile, smsCode);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
protected String obtainSmsCode(HttpServletRequest request) {
return request.getParameter(smsCodeParameter);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setMobileParameter(String mobileParameter) {
this.mobileParameter = mobileParameter;
}
public void setSmsCodeParameter(String smsCodeParameter) {
this.smsCodeParameter = smsCodeParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
```
3. 自定义身份验证Token
创建一个自定义的身份验证Token,用于存储用户输入的手机号码和验证码。
```
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 420L;
private final Object principal;
private Object credentials;
public SmsCodeAuthenticationToken(String mobile, String smsCode) {
super(null);
this.principal = mobile;
this.credentials = smsCode;
setAuthenticated(false);
}
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = null;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return credentials;
}
@Override
public Object getPrincipal() {
return principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
}
```
4. 自定义身份验证提供者
创建一个自定义的身份验证提供者,用于根据手机号码和验证码验证用户身份。
```
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
String mobile = authenticationToken.getPrincipal().toString();
String smsCode = authenticationToken.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByMobile(mobile);
if (userDetails == null) {
throw new UsernameNotFoundException("User not found by mobile: " + mobile);
}
// 验证短信验证码
if (!"123456".equals(smsCode)) {
throw new BadCredentialsException("Invalid sms code");
}
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}
```
5. 配置身份验证安全
创建一个配置类,用于添加自定义的身份验证过滤器和身份验证提供者。
```
@Configuration
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
http.authenticationProvider(smsCodeAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
```
6. 自定义用户详情服务
创建一个自定义的用户详情服务,用于根据手机号码获取用户信息。
```
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
public UserDetails loadUserByMobile(String mobile) throws UsernameNotFoundException {
// TODO: 根据手机号码获取用户信息
return null;
}
}
```
7. 添加验证码生成和发送逻辑
在登录页面中添加生成和发送验证码的逻辑,当用户点击“发送验证码”按钮时,调用后台接口生成验证码并发送到用户手机上。
```
@PostMapping("/sms/code")
@ResponseBody
public ResponseEntity<Object> createSmsCode(@RequestParam("mobile") String mobile) {
// TODO: 生成验证码并发送到用户手机上
return ResponseEntity.ok().build();
}
```
最后,启动应用程序,访问登录页面,输入手机号码和验证码即可完成登录。
微信小程序使用springsecurity和jwt实现权限验证java代码
以下是微信小程序使用Spring Security和JWT实现权限验证的Java代码示例:
1. 配置Spring Security
创建一个SecurityConfig类,配置Spring Security的基本设置和JWT过滤器:
```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtAuthorizationTokenFilter jwtAuthorizationTokenFilter;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtAuthorizationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl();
}
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers(HttpMethod.POST, "/api/auth/login");
}
}
```
2. 创建JWT工具类
创建一个JwtTokenUtil类,用于处理JWT的生成、解析和验证:
```java
@Component
public class JwtTokenUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "iat";
private static final String CLAIM_KEY_EXPIRED = "exp";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String token) {
String username;
try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
public Date getCreatedDateFromToken(String token) {
Date created;
try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
} catch (Exception e) {
created = null;
}
return created;
}
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean canTokenBeRefreshed(String token) {
return !isTokenExpired(token);
}
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
public Boolean validateToken(String token, UserDetails userDetails) {
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(token);
return (
username.equals(user.getUsername())
&& !isTokenExpired(token));
}
}
```
3. 创建JWT过滤器
创建一个JwtAuthorizationTokenFilter类,用于处理JWT的解析和验证:
```java
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
logger.debug("processing authentication for '{}'", request.getRequestURL());
final String requestHeader = request.getHeader("Authorization");
String username = null;
String authToken = null;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (IllegalArgumentException e) {
logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
logger.warn("the token is expired and not valid anymore", e);
}
} else {
logger.warn("couldn't find bearer string, will ignore the header");
}
logger.debug("checking authentication for user '{}'", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user '{}', setting security context", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
```
4. 创建JWT用户类
创建一个JwtUser类,实现Spring Security的UserDetails接口:
```java
public class JwtUser implements UserDetails {
private final Long id;
private final String username;
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
private final boolean enabled;
private final Date lastPasswordResetDate;
public JwtUser(
Long id,
String username,
String password,
Collection<? extends GrantedAuthority> authorities,
boolean enabled,
Date lastPasswordResetDate
) {
this.id = id;
this.username = username;
this.password = password;
this.authorities = authorities;
this.enabled = enabled;
this.lastPasswordResetDate = lastPasswordResetDate;
}
@JsonIgnore
public Long getId() {
return id;
}
@Override
public String getUsername() {
return username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public String getPassword() {
return password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public boolean isEnabled() {
return enabled;
}
@JsonIgnore
public Date getLastPasswordResetDate() {
return lastPasswordResetDate;
}
}
```
5. 创建登录控制器
创建一个AuthController类,用于处理用户登录和令牌的生成:
```java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) throws AuthenticationException {
logger.info("authenticating user '{}'", authenticationRequest.getUsername());
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
}
```
6. 创建异常处理器
创建一个JwtAuthenticationEntryPoint类,用于处理不允许访问的请求:
```java
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
logger.error("unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
```
7. 创建请求对象
创建一个JwtAuthenticationRequest类,用于保存用户登录请求:
```java
public class JwtAuthenticationRequest implements Serializable {
private static final long serialVersionUID = -8445943548965154778L;
@NotBlank
private String username;
@NotBlank
private String password;
public JwtAuthenticationRequest() {
super();
}
public JwtAuthenticationRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
```
8. 创建响应对象
创建一个JwtAuthenticationResponse类,用于保存令牌响应:
```java
public class JwtAuthenticationResponse implements Serializable {
private static final long serialVersionUID = 1250166508152483573L;
private final String token;
public JwtAuthenticationResponse(String token) {
this.token = token;
}
public String getToken() {
return this.token;
}
}
```
以上是微信小程序使用Spring Security和JWT实现权限验证的Java代码示例,希望能对你有所帮助。
阅读全文