Springboot整合security与jwt

本文介绍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注解
Springboot整合security与jwt_第1张图片

现在整合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();
    }
}

Springboot整合security与jwt_第2张图片

现在整合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
Springboot整合security与jwt_第3张图片
没有token
Springboot整合security与jwt_第4张图片

根据token获取信息
Springboot整合security与jwt_第5张图片

到此结束
github:https://github.com/Acumes/spring-security-jwt
备用下载地址:https://download.csdn.net/download/a295277302/10584376

代码参考自:https://github.com/zhangxd1989/springboot-dubbox

你可能感兴趣的:(SPRING)