天天看點

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.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>.....</dependencies>
           

先整合資料源與mybatis

application.properties

server.port=

#htf datasource
spring.datasource.htf.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.htf.url=jdbc:mysql://localhost:/platform?characterEncoding=utf8&allowMultiQueries=true
spring.datasource.htf.username=root
spring.datasource.htf.password=
spring.datasource.htf.driverClassName=com.mysql.jdbc.Driver
spring.datasource.htf.filters=stat,config 
spring.datasource.htf.maxActive=
spring.datasource.htf.initialSize=
spring.datasource.htf.maxWait=
spring.datasource.htf.minIdle=
spring.datasource.htf.timeBetweenEvictionRunsMillis=
spring.datasource.htf.minEvictableIdleTimeMillis=
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=
spring.datasource.htf.maxPoolPreparedStatementPerConnectionSize=



spring.redis.database=
spring.redis.host=
spring.redis.port=
spring.redis.timeout=
spring.redis.jedis.pool.max-active=
spring.redis.jedis.pool.max-idle=
spring.redis.jedis.pool.min-idle=
spring.redis.jedis.pool.max-wait=-

logging.level.logging.com.htf.dao=debug

security.jwt.secret=k09BQnaF
#20Days
security.jwt.expiration=

           

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

現在整合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

現在整合Redis

RedisConfig

@Configuration
public class RedisConfig {

    /**
     * Redis repository redis repository.
     *
     * @param redisTemplate the redis template
     * @return the redis repository
     */
    @Bean
    public RedisRepository redisRepository(RedisTemplate<String, String> 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();
    }

    @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, );
        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<String, Object> 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<String, Object> tokenMap = new HashMap<>();
        tokenMap.put("access_token", token);
        tokenMap.put("expires_in", jwtTokenUtil.getExpiration());
        tokenMap.put("token_type", TokenUtil.TOKEN_TYPE_BEARER);

        Map<String, Object> 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<String, Object> refreshAndGetAuthenticationToken(
        HttpServletRequest request) {

        String tokenHeader = request.getHeader(AuthenticationTokenFilter.TOKEN_HEADER);
        String token = tokenHeader.split(" ")[];

        //重新生成Token
        String username = jwtTokenUtil.getUsernameFromToken(token);
        final UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
        final String refreshedToken = jwtTokenUtil.generateToken(userDetails);

        Map<String, Object> tokenMap = new HashMap<>();
        tokenMap.put("access_token", refreshedToken);
        tokenMap.put("expires_in", jwtTokenUtil.getExpiration());
        tokenMap.put("token_type", TokenUtil.TOKEN_TYPE_BEARER);

        Map<String, Object> 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<String, Object> deleteAuthenticationToken(
        HttpServletRequest request) {

        String tokenHeader = request.getHeader(AuthenticationTokenFilter.TOKEN_HEADER);
        String token = tokenHeader.split(" ")[];

        //移除token
        jwtTokenUtil.removeToken(token);

        Map<String, Object> 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<String, Object> 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<String, Object> handleBusinessException(DisabledException ex) {
        //使用者被停用
        return makeErrorMessage(ReturnCode.DISABLED_USER, "User Disabled", ex.getMessage());
    }

}
           

啟動項目,擷取token

浏覽器打開:http://localhost:8082/swagger-ui.html

Springboot整合security與jwt

沒有token

Springboot整合security與jwt

根據token擷取資訊

Springboot整合security與jwt

到此結束

github:https://github.com/Acumes/spring-security-jwt

備用下載下傳位址:https://download.csdn.net/download/a295277302/10584376

代碼參考自:https://github.com/zhangxd1989/springboot-dubbox

繼續閱讀