Spring Boot+Spring Security + JWT集成
目录
- 1、认证
- 2、授权
- 3、防XSS攻击
- 4、设置跨域访问
- 5、目录结构
- 6、完整代码
- 6.1、pom.xml
- 6.2、src/main/java/com/cnblogs/javalouvre/App.java
- 6.3、src/main/java/com/cnblogs/javalouvre/config/SecurityConfig.java
- 6.4、src/main/java/com/cnblogs/javalouvre/dto/AjaxResult.java
- 6.5、src/main/java/com/cnblogs/javalouvre/exception/GlobalExceptionHandler.java
- 6.6、src/main/java/com/cnblogs/javalouvre/service/TokenService.java
- 6.7、src/main/java/com/cnblogs/javalouvre/util/Constants.java
- 6.8、src/main/java/com/cnblogs/javalouvre/util/HttpUtils.java
- 6.9、src/main/java/com/cnblogs/javalouvre/util/JacksonUtils.java
- 6.10、src/main/java/com/cnblogs/javalouvre/web/BaseController.java
- 6.11、src/main/java/com/cnblogs/javalouvre/web/IndexController.java
- 6.12、src/main/resources/application.yml
1、认证
1.1、配置登录地址
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = hs.authorizeRequests();
authorizeRequests.antMatchers("/login").anonymous();
1.2、初始化账号信息
初始化登录账号、密码、用户角色等信息。
/**
* 初始化登录用户信息
*/
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withUsername("admin") // 账号
.password("$2a$10$nisfRsnoOqL2M44mZKlIQuYjTXPxYQBYM3dGMWfca1dYUBMV7e7iS") // `admin123`加密后字符串
.roles("ADMIN") // 角色
.build();
return new InMemoryUserDetailsManager(userDetails);
}
1.3、初始化账号信息
@PostMapping(path = "/login", produces = APPLICATION_JSON_VALUE)
public AjaxResult login(String username, String password) {
if (isBlank(username)) {
return AjaxResult.error("登录失败", "账号不可为空");
}
if (isBlank(password)) {
return AjaxResult.error("登录失败", "密码不可为空");
}
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (isNull(userDetails) || !passwordEncoder.matches(password, userDetails.getPassword())) {
return AjaxResult.error("登录失败", "账号或密码错误");
}
return AjaxResult.ok("登录成功", tokenService.createToken(username));
}
2、授权
授权通过过滤器实现
hs.addFilterAfter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String headerValue = request.getHeader(header);
String username = null;
if (isNotBlank(headerValue) && startsWith(headerValue, TOKEN_PREFIX)) {
username = tokenService.getSubject(headerValue.replaceAll(TOKEN_PREFIX, EMPTY));
}
if (isNotBlank(username) && isNull(getContext().getAuthentication())) {
UserDetails userDetails = userDetailsService().loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}, UsernamePasswordAuthenticationFilter.class);
3、防XSS攻击
3.1、解决方案
解决方案有多种,第一种就是自定一个过滤器,配置时请将这个 filter 放在第一位。
第二种,借助Spring提供的方式。
3.2、 自定义 BaseController.java 控制器,所有控制器继承该类
package com.cnblogs.javalouvre.web;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import java.beans.PropertyEditorSupport;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.util.HtmlUtils;
public class BaseController {
@InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
@Override
public String getAsText() {
Object text = getValue();
return isNull(text) ? EMPTY : text.toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (isNull(text)) {
setValue(null);
} else {
setValue(HtmlUtils.htmlEscape(text));
}
}
});
}
}
4、设置跨域访问
4.1、注册 org.springframework.web.cors.CorsConfigurationSource 实例
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean("corsConfigurationSource")
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");// 设置访问源地址
config.addAllowedHeader("*");// 设置访问源请求头
config.addAllowedMethod("*");// 设置访问源请求方法
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);// 对接口配置跨域设置
return configSource;
}
}
4.2、设置开启跨域 hs.cors(Customizer.withDefaults())
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity hs) throws Exception {
// by default uses a Bean by the name of corsConfigurationSource
hs.cors(Customizer.withDefaults());
}
}
5、目录结构
│ pom.xml
│
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─cnblogs
│ │ └─javalouvre
│ │ │ App.java
│ │ │
│ │ ├─config
│ │ │ SecurityConfig.java
│ │ │
│ │ ├─dto
│ │ │ AjaxResult.java
│ │ │
│ │ ├─exception
│ │ │ GlobalExceptionHandler.java
│ │ │
│ │ ├─service
│ │ │ TokenService.java
│ │ │
│ │ ├─util
│ │ │ Constants.java
│ │ │ HttpUtils.java
│ │ │ JacksonUtils.java
│ │ │
│ │ └─web
│ │ BaseController.java
│ │ IndexController.java
│ │
│ └─resources
│ application.yml
│
└─test
├─java
└─resources
6、完整代码
6.1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.12.RELEASE</version>
<relativePath />
</parent>
<artifactId>spring-boot-springsecurity-jwt</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
6.2、src/main/java/com/cnblogs/javalouvre/App.java
package com.cnblogs.javalouvre;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
6.3、src/main/java/com/cnblogs/javalouvre/config/SecurityConfig.java
package com.cnblogs.javalouvre.config;
import static com.cnblogs.javalouvre.dto.AjaxResult.forbidden;
import static com.cnblogs.javalouvre.util.Constants.TOKEN_PREFIX;
import static com.cnblogs.javalouvre.util.HttpUtils.getToken;
import static com.cnblogs.javalouvre.util.HttpUtils.write;
import static com.cnblogs.javalouvre.util.JacksonUtils.serialize;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.startsWith;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.security.core.context.SecurityContextHolder.getContext;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.Customizer;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.OncePerRequestFilter;
import com.cnblogs.javalouvre.dto.AjaxResult;
import com.cnblogs.javalouvre.service.TokenService;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Override
protected void configure(HttpSecurity hs) throws Exception {
hs.csrf().disable();
hs.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
write(response, serialize(AjaxResult.class, forbidden("认证失败,无法访问该资源")));
}
});
hs.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
hs.headers().frameOptions().disable();
hs.cors(Customizer.withDefaults()); // by default uses a Bean by the name of corsConfigurationSource
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = hs.authorizeRequests();
authorizeRequests.antMatchers("/login").anonymous();
authorizeRequests.antMatchers(GET, "/*.html").permitAll();
authorizeRequests.antMatchers(GET, "/**/*.js").permitAll();
authorizeRequests.antMatchers(GET, "/**/*.css").permitAll();
authorizeRequests.anyRequest().authenticated();
hs.logout().logoutUrl("/logout").logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String username = tokenService.getSubject(getToken(request, header));
logger.info("username = {}", username);
}
});
hs.addFilterAfter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String headerValue = request.getHeader(header);
String username = null;
if (isNotBlank(headerValue) && startsWith(headerValue, TOKEN_PREFIX)) {
username = tokenService.getSubject(headerValue.replaceAll(TOKEN_PREFIX, EMPTY));
}
if (isNotBlank(username) && isNull(getContext().getAuthentication())) {
UserDetails userDetails = userDetailsService().loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}, UsernamePasswordAuthenticationFilter.class);
}
/**
* 认证
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 加密
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 初始化登录用户信息
*/
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withUsername("admin") // 账号
.password("$2a$10$nisfRsnoOqL2M44mZKlIQuYjTXPxYQBYM3dGMWfca1dYUBMV7e7iS") // `admin123`加密后字符串
.roles("ADMIN") // 角色
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean("corsConfigurationSource")
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");// 设置访问源地址
config.addAllowedHeader("*");// 设置访问源请求头
config.addAllowedMethod("*");// 设置访问源请求方法
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);// 对接口配置跨域设置
return configSource;
}
@Autowired
private TokenService tokenService;
@Value("${token.header}")
private String header;
}
6.4、src/main/java/com/cnblogs/javalouvre/dto/AjaxResult.java
package com.cnblogs.javalouvre.dto;
import static java.util.Objects.nonNull;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK;
public class AjaxResult extends java.util.HashMap<String, Object> {
private static final long serialVersionUID = 242180064461040653L;
// 状态码
private static final String CODE_TAG = "code";
// 返回内容
private static final String MSG_TAG = "msg";
// 数据对象
private static final String DATA_TAG = "data";
public AjaxResult() {
// DO NOTHING
}
public AjaxResult(int code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
public AjaxResult(int code, String msg, Object data) {
this(code, msg);
if (nonNull(data)) {
super.put(DATA_TAG, data);
}
}
public static AjaxResult ok() {
return AjaxResult.ok("操作成功");
}
public static AjaxResult ok(Object object) {
return AjaxResult.ok("操作成功", object);
}
public static AjaxResult ok(String msg) {
return AjaxResult.ok(msg, null);
}
public static AjaxResult ok(String msg, Object data) {
return new AjaxResult(SC_OK, msg, data);
}
public static AjaxResult error() {
return AjaxResult.error("操作失败");
}
public static AjaxResult error(Object object) {
return AjaxResult.error("操作失败", object);
}
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(SC_INTERNAL_SERVER_ERROR, msg, data);
}
public static AjaxResult forbidden() {
return AjaxResult.forbidden("操作禁止");
}
public static AjaxResult forbidden(Object object) {
return AjaxResult.forbidden("操作禁止", object);
}
public static AjaxResult forbidden(String msg) {
return AjaxResult.forbidden(msg, null);
}
public static AjaxResult forbidden(String msg, Object data) {
return new AjaxResult(SC_FORBIDDEN, msg, data);
}
}
6.5、src/main/java/com/cnblogs/javalouvre/exception/GlobalExceptionHandler.java
package com.cnblogs.javalouvre.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.cnblogs.javalouvre.dto.AjaxResult;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(SignatureException.class)
@ResponseBody
public AjaxResult signatureException(SignatureException e) {
if (logger.isErrorEnabled()) {
logger.error("Token为空", e);
}
return AjaxResult.error("Token为空");
}
@ResponseBody
@ExceptionHandler(ExpiredJwtException.class)
public AjaxResult expiredJwtException(ExpiredJwtException e) {
if (logger.isErrorEnabled()) {
logger.error("Token过期", e);
}
return AjaxResult.error("Token过期");
}
@ResponseBody
@ExceptionHandler(MalformedJwtException.class)
public AjaxResult malformedJwtException(MalformedJwtException e) {
if (logger.isErrorEnabled()) {
logger.error("Token数据错误", e);
}
return AjaxResult.error("Token数据错误");
}
}
6.6、src/main/java/com/cnblogs/javalouvre/service/TokenService.java
package com.cnblogs.javalouvre.service;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class TokenService {
/**
* 生成Token
*
* @param sub
* @return
*/
public String createToken(String sub) {
Calendar calendar = GregorianCalendar.getInstance();
Date iat = calendar.getTime();
calendar.add(GregorianCalendar.MINUTE, amount);
Date exp = calendar.getTime();
return Jwts.builder()//
.setHeaderParam("typ", "JWT")// 令牌类型
.setSubject(sub)// 主题
.setIssuedAt(iat) // 签发时间
.setExpiration(exp)// 过期时间
.signWith(SignatureAlgorithm.HS512, secretKey)// 签名算法、秘钥
.compact();
}
/**
* 获取Token中注册信息
*
* @param token
* @return
*/
public Claims getBody(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
}
/**
* 获取签发日期
*
* @param token
* @return
*/
public Date getIssuedAt(String token) {
return getBody(token).getIssuedAt();
}
/**
* 获取过期时间
*
* @param token
* @return
*/
public Date getExpiration(String token) {
return getBody(token).getExpiration();
}
/**
* 获取主题信息
*
* @param token
* @return
*/
public String getSubject(String token) {
return getBody(token).getSubject();
}
// 令牌秘钥
@Value("${token.secret}")
private String secretKey;
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int amount;
}
6.7、src/main/java/com/cnblogs/javalouvre/util/Constants.java
package com.cnblogs.javalouvre.util;
/**
* 常量
*/
public interface Constants {
String TOKEN_PREFIX = "Bearer ";
}
6.8、src/main/java/com/cnblogs/javalouvre/util/HttpUtils.java
package com.cnblogs.javalouvre.util;
import static com.cnblogs.javalouvre.util.Constants.TOKEN_PREFIX;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.removeStart;
import static org.apache.commons.lang3.StringUtils.startsWith;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import io.jsonwebtoken.MalformedJwtException;
public final class HttpUtils {
private HttpUtils() {
}
public static void write(HttpServletResponse response, String message) {
try {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write(message);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getToken(HttpServletRequest request, String headerName) {
String headerValue = request.getHeader(headerName);
if (isBlank(headerValue) || !startsWith(headerValue, TOKEN_PREFIX)) {
throw new MalformedJwtException(StringUtils.EMPTY);
}
return removeStart(headerValue, TOKEN_PREFIX);
}
}
6.9、src/main/java/com/cnblogs/javalouvre/util/JacksonUtils.java
package com.cnblogs.javalouvre.util;
import java.io.IOException;
import java.util.Objects;
import org.apache.commons.lang3.SerializationException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public final class JacksonUtils {
private static final Logger log = LoggerFactory.getLogger(JacksonUtils.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
private JacksonUtils() {
}
public static String serialize(Class<?> serializationView, Object value) {
if (Objects.isNull(value)) {
return StringUtils.EMPTY;
}
try {
return objectMapper.writerWithView(serializationView).writeValueAsString(value);
} catch (JsonProcessingException e) {
log.error("COULD NOT WRITE JSON", e);
throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
}
}
public static <T> T deserialize(Class<T> type, String src) {
if (StringUtils.isBlank(src)) {
return null;
}
try {
return objectMapper.readerFor(type).readValue(src);
} catch (IOException e) {
log.error("COULD NOT READ JSON", e);
throw new SerializationException("Could not read JSON: " + e.getMessage(), e);
}
}
}
6.10、src/main/java/com/cnblogs/javalouvre/web/BaseController.java
package com.cnblogs.javalouvre.web;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import java.beans.PropertyEditorSupport;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.util.HtmlUtils;
public class BaseController {
@InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
@Override
public String getAsText() {
Object text = getValue();
return isNull(text) ? EMPTY : text.toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (isNull(text)) {
setValue(null);
} else {
setValue(HtmlUtils.htmlEscape(text));
}
}
});
}
}
6.11、src/main/java/com/cnblogs/javalouvre/web/IndexController.java
package com.cnblogs.javalouvre.web;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cnblogs.javalouvre.dto.AjaxResult;
import com.cnblogs.javalouvre.service.TokenService;
import com.cnblogs.javalouvre.util.HttpUtils;
@RestController
public class IndexController extends BaseController {
@PostMapping(path = "/login", produces = APPLICATION_JSON_VALUE)
public AjaxResult login(String username, String password) {
if (isBlank(username)) {
return AjaxResult.error("登录失败", "账号不可为空");
}
if (isBlank(password)) {
return AjaxResult.error("登录失败", "密码不可为空");
}
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (isNull(userDetails) || !passwordEncoder.matches(password, userDetails.getPassword())) {
return AjaxResult.error("登录失败", "账号或密码错误");
}
return AjaxResult.ok("登录成功", tokenService.createToken(username));
}
@PreAuthorize("hasAnyRole('ADMIN')")
@GetMapping(path = "/index", produces = APPLICATION_JSON_VALUE)
public AjaxResult execute(HttpServletRequest request) {
return AjaxResult.ok(tokenService.getSubject(HttpUtils.getToken(request, header)));
}
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenService tokenService;
@Value("${token.header}")
private String header;
}
6.12、src/main/resources/application.yml
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥(自定义)
secret: 6F3931D35F0395DF82B032B8019AC57D
# 令牌有效期(默认30分钟)
expireTime: 30
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %-5level %logger{20} - [%method,%line] - %msg%n'
level:
root: info
'[org.springframework.beans.factory.annotation]': trace
'[org.springframework.security.web]': trace
'[org.springframework.web.servlet.mvc]': trace
'[com.cnblogs.javalouvre]': trace
-----------------------------------------------------------------------------------------------------------
薔薇猛虎皆成個性,陽光雨露俱是天恩!
薔薇猛虎皆成個性,陽光雨露俱是天恩!
浙公网安备 33010602011771号