本文介绍springboot2.0.4整合security,jwt,redis,mybatis,druid,swagger-ui
先上github:https://github.com/Acumes/spring-security-jwt
备用下载地址:https://download.csdn.net/download/a295277302/10584376
下面介绍项目中的内容
部分pom.xml
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.4.RELEASEversion>
<relativePath/>
parent>
<dependencies>.....dependencies>
先整合数据源与mybatis
application.properties
server.port=8082
#htf datasource
spring.datasource.htf.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.htf.url=jdbc:mysql://localhost:3306/platform?characterEncoding=utf8&allowMultiQueries=true
spring.datasource.htf.username=root
spring.datasource.htf.password=123456
spring.datasource.htf.driverClassName=com.mysql.jdbc.Driver
spring.datasource.htf.filters=stat,config
spring.datasource.htf.maxActive=20
spring.datasource.htf.initialSize=1
spring.datasource.htf.maxWait=6000
spring.datasource.htf.minIdle=1
spring.datasource.htf.timeBetweenEvictionRunsMillis=60000
spring.datasource.htf.minEvictableIdleTimeMillis=300000
spring.datasource.htf.validationQuery=select 'x'
spring.datasource.htf.testWhileIdle=true
spring.datasource.htf.testOnBorrow=true
spring.datasource.htf.testOnReturn=true
spring.datasource.htf.poolPreparedStatements=true
spring.datasource.htf.maxOpenPreparedStatements=20
spring.datasource.htf.maxPoolPreparedStatementPerConnectionSize=20
spring.redis.database=2
spring.redis.host=192.168.1.105
spring.redis.port=6379
spring.redis.timeout=5000
spring.redis.jedis.pool.max-active=300
spring.redis.jedis.pool.max-idle=100
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-wait=-1
logging.level.logging.com.htf.dao=debug
security.jwt.secret=k09BQnaF
#20Days
security.jwt.expiration=1728000
DataSourceConfig
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "htfDataSource")
@ConfigurationProperties("spring.datasource.htf")
public DruidDataSource htfDataSource() {
return new DruidDataSource();
}
}
添加mybatis配置
MybatisDataSourceConfig
@Configuration
@MapperScan(basePackages = "com.htf.**.dao", sqlSessionFactoryRef = "mybatisSqlSessionFactory")
public class MybatisDataSourceConfig {
//事务管理器
@Bean(name = "mybatisTransactionManager")
@Primary
public DataSourceTransactionManager mybatisTransactionManager(@Qualifier("htfDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
//会话工厂
@Bean(name = "mybatisSqlSessionFactory")
@Primary
public SqlSessionFactory mybatisSqlSessionFactory(@Qualifier("htfDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sf = new SqlSessionFactoryBean();
sf.setDataSource(dataSource);
sf.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:repository/**/*Mapper.xml"));
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);
sf.setPlugins(new Interceptor[]{pageInterceptor});
return sf.getObject();
}
}
接着整合Druid
DruidStatViewServlet
@WebServlet(urlPatterns = "/druid/*",
initParams = {
// @WebInitParam(name="allow",value="192.168.199.227,127.0.0.1"),// IP白名单 (没有配置或者为空,则允许所有访问)
// @WebInitParam(name="deny",value="192.168.199.237"),// IP黑名单 (存在共同时,deny优先于allow)
@WebInitParam(name = "loginUsername", value = "admin"),// 用户名
@WebInitParam(name = "loginPassword", value = "admin"),// 密码
@WebInitParam(name = "resetEnable", value = "false")// 禁用HTML页面上的“Reset All”功能
})
public class DruidStatViewServlet extends StatViewServlet {
}
整合druid需要注意都是要在Application启动类上添加@ServletComponentScan注解
现在整合Swagger-ui
Swagger2Config
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.htf"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("springboot利用swagger构建api文档")
.description("简单优雅的restfun风格,https://my.csdn.net/a295277302")
.termsOfServiceUrl("https://my.csdn.net/a295277302")
.version("1.0")
.build();
}
}
现在整合Redis
RedisConfig
@Configuration
public class RedisConfig {
/**
* Redis repository redis repository.
*
* @param redisTemplate the redis template
* @return the redis repository
*/
@Bean
public RedisRepository redisRepository(RedisTemplate redisTemplate) {
return new RedisRepository(redisTemplate);
}
}
注入Redis,参考RedisRepository,跟RedisConfig配置在一块。
现在整合Security
AbstractWebSecurityConfig
public class AbstractWebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 用户信息服务
*/
@Autowired
private UserDetailsService userDetailsServiceImpl;
/**
* Password encoder password encoder.
*
* @return the password encoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(8);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsServiceImpl)
.passwordEncoder(this.passwordEncoder())
;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* Authentication token filter bean authentication token filter.
*
* @return the authentication token filter
*/
@Bean
public AuthenticationTokenFilter authenticationTokenFilterBean() {
return new AuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
// Custom JWT based security filter
security
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends AbstractWebSecurityConfig {
@Override
public void configure(WebSecurity web) throws Exception {
//忽略权限校验的访问路径
web
.ignoring()
.antMatchers(
"/hello",
"/favicon.ico",
"/swagger**/**",
"/*/api-docs",
"/webjars/**",
"/druid/**"
)
.antMatchers(HttpMethod.POST, "/*/user")
;
}
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/auth/token").permitAll();
super.configure(security);
}
}
过滤器
AuthenticationTokenFilter
public class AuthenticationTokenFilter extends GenericFilterBean {
/**
* 携带Token的HTTP头
*/
public static final String TOKEN_HEADER = "Authorization";
/**
* Token工具类
*/
@Autowired
private AbstractTokenUtil jwtTokenUtil;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authHeader = httpRequest.getHeader(TOKEN_HEADER);
if (authHeader == null || !authHeader.startsWith(AbstractTokenUtil.TOKEN_TYPE_BEARER)) {
chain.doFilter(request, response);
return;
}
final String authToken = StringHelper.substring(authHeader, 7);
String username = StringHelper.isNotBlank(authToken) ? jwtTokenUtil.getUsernameFromToken(authToken) : null;
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null && jwtTokenUtil.validateToken(authToken)) {
UserDetails userDetails = jwtTokenUtil.getUserDetails(authToken);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
错误提示
MyAuthenticationEntryPoint
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// This is invoked when user tries to access a secured REST resource without supplying any credentials
// We should just send a 401 Unauthorized response because there is no 'login page' to redirect to
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授权");
}
}
登录控制Controller
AuthenticationController
@RestController
@RequestMapping("/auth")
@Api(tags = "权限管理")
public class AuthenticationController extends BaseController{
/**
* 权限管理
*/
@Autowired
private AuthenticationManager authenticationManager;
/**
* 用户信息服务
*/
@Autowired
private UserDetailsService userDetailsServiceImpl;
/**
* Token工具类
*/
@Autowired
private TokenUtil jwtTokenUtil;
/**
* Create authentication token map.
*
* @param username the username
* @param password the password
* @return the map
*/
@PostMapping(value = "/token", produces = "application/json; charset=UTF-8")
@ApiOperation(value = "获取token")
public Map createAuthenticationToken(
@ApiParam(required = true, value = "用户名") @RequestParam("username") String username,
@ApiParam(required = true, value = "密码") @RequestParam("password") String password
) {
//完成授权
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
final UserDetails userDetails = (UserDetails) authentication.getPrincipal();
final String token = jwtTokenUtil.generateToken(userDetails); //生成Token
Map tokenMap = new HashMap<>();
tokenMap.put("access_token", token);
tokenMap.put("expires_in", jwtTokenUtil.getExpiration());
tokenMap.put("token_type", TokenUtil.TOKEN_TYPE_BEARER);
Map message = new HashMap<>();
message.put(Message.RETURN_FIELD_CODE, ReturnCode.SUCCESS);
message.put(Message.RETURN_FIELD_DATA, tokenMap);
return message;
}
/**
* Refresh and get authentication token map.
*
* @param request the request
* @return the map
*/
@GetMapping(value = "/refresh", produces = "application/json; charset=UTF-8")
@ApiOperation(value = "刷新token")
@ApiImplicitParams(
{
@ApiImplicitParam(name = "Authorization", required = true, paramType = "header",
dataType = "string", value = "authorization header", defaultValue = "Bearer ")
}
)
public Map refreshAndGetAuthenticationToken(
HttpServletRequest request) {
String tokenHeader = request.getHeader(AuthenticationTokenFilter.TOKEN_HEADER);
String token = tokenHeader.split(" ")[1];
//重新生成Token
String username = jwtTokenUtil.getUsernameFromToken(token);
final UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
final String refreshedToken = jwtTokenUtil.generateToken(userDetails);
Map tokenMap = new HashMap<>();
tokenMap.put("access_token", refreshedToken);
tokenMap.put("expires_in", jwtTokenUtil.getExpiration());
tokenMap.put("token_type", TokenUtil.TOKEN_TYPE_BEARER);
Map message = new HashMap<>();
message.put(Message.RETURN_FIELD_CODE, ReturnCode.SUCCESS);
message.put(Message.RETURN_FIELD_DATA, tokenMap);
return message;
}
/**
* Delete authentication token map.
*
* @param request the request
* @return the map
*/
@DeleteMapping(value = "/token", produces = "application/json; charset=UTF-8")
@ApiOperation(value = "清空token")
@ApiImplicitParams(
{
@ApiImplicitParam(name = "Authorization", required = true, paramType = "header",
dataType = "string", value = "authorization header", defaultValue = "Bearer ")
}
)
public Map deleteAuthenticationToken(
HttpServletRequest request) {
String tokenHeader = request.getHeader(AuthenticationTokenFilter.TOKEN_HEADER);
String token = tokenHeader.split(" ")[1];
//移除token
jwtTokenUtil.removeToken(token);
Map message = new HashMap<>();
message.put(Message.RETURN_FIELD_CODE, ReturnCode.SUCCESS);
return message;
}
/**
* Handle business exception map.
*
* @param ex the ex
* @return the map
*/
@ExceptionHandler(BadCredentialsException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map handleBusinessException(BadCredentialsException ex) {
//用户名或密码错误
return makeErrorMessage(ReturnCode.INVALID_GRANT, "Bad credentials", ex.getMessage());
}
/**
* Handle business exception map.
*
* @param ex the ex
* @return the map
*/
@ExceptionHandler(DisabledException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public Map handleBusinessException(DisabledException ex) {
//用户被停用
return makeErrorMessage(ReturnCode.DISABLED_USER, "User Disabled", ex.getMessage());
}
}
启动项目,获取token
浏览器打开:http://localhost:8082/swagger-ui.html
没有token
到此结束
github:https://github.com/Acumes/spring-security-jwt
备用下载地址:https://download.csdn.net/download/a295277302/10584376
代码参考自:https://github.com/zhangxd1989/springboot-dubbox