博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 鉴权之—— springboot2.0.4+mybatis 整合的完整用例
阅读量:6187 次
发布时间:2019-06-21

本文共 13593 字,大约阅读时间需要 45 分钟。

自上一篇文章的基础上,我做了一波springboot2.0.4+mybatis 的整合。

参考文章:    

  源码地址:

               码云:https://gitee.com/region/spring-security-oauth-example/tree/master/spring-security-jwt

springboot2.0.4+mybatis pom.xml:

 这里由于springboot2.0.4没有默认的passwordencoder,也就是说我们登录不能明文登录,所以为了方便期间,我直接使用了数据库。

org.apache.commons
commons-lang3
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.0
mysql
mysql-connector-java

config配置修改:

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import com.jwt.server.filter.JwtAuthenticationFilter;import com.jwt.server.filter.JwtLoginFilter;import com.jwt.server.provider.CustomAuthenticationProvider;/** * 通过SpringSecurity的配置,将JWTLoginFilter,JWTAuthenticationFilter组合在一起 *  * @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 在springboot1.5.8的时候该注解是可以用的 *                                                  具体看源码 * @author zyl * */@Configurationpublic class SecurityConfiguration extends WebSecurityConfigurerAdapter {    @Qualifier("userDetailServiceImpl")    @Autowired    private UserDetailsService userDetailsService;    @Autowired    private BCryptPasswordEncoder bCryptPasswordEncoder;    @Override    public void configure(WebSecurity web) throws Exception {        super.configure(web);    }    @Override    protected void configure(HttpSecurity http) throws Exception {        // 自定义 默认        http.cors().and().csrf().disable().authorizeRequests().antMatchers("/users/signup").permitAll().anyRequest()                .authenticated().and().addFilter(new JwtLoginFilter(authenticationManager()))// 默认登录过滤器                .addFilter(new JwtAuthenticationFilter(authenticationManager()));// 自定义过滤器    }        // 该方法是登录的时候会进入    @Override    public void configure(AuthenticationManagerBuilder auth) throws Exception {//        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());        // 使用自定义身份验证组件   手动注入加密类        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService, bCryptPasswordEncoder));    }}

自定义身份验证组件

package com.jwt.server.provider;import java.util.ArrayList;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/** * 自定义身份认证验证组件 * @author zyl * */public class CustomAuthenticationProvider implements AuthenticationProvider {    private UserDetailsService userDetailsService;    private BCryptPasswordEncoder bCryptPasswordEncoder;    public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder){        this.userDetailsService = userDetailsService;        this.bCryptPasswordEncoder = bCryptPasswordEncoder;    }    @Override    public Authentication authenticate(Authentication authentication) throws AuthenticationException {        // 获取认证的用户名 & 密码        String name = authentication.getName();        String password = authentication.getCredentials().toString();        // 认证逻辑        UserDetails userDetails = userDetailsService.loadUserByUsername(name);        if (null != userDetails) {            if (bCryptPasswordEncoder.matches(password, userDetails.getPassword())) {                // 这里设置权限和角色                ArrayList
authorities = new ArrayList<>(); authorities.add( new GrantedAuthorityImpl("ROLE_ADMIN")); authorities.add( new GrantedAuthorityImpl("ROLE_API")); authorities.add( new GrantedAuthorityImpl("AUTH_WRITE")); // 生成令牌 这里令牌里面存入了:name,password,authorities, 当然你也可以放其他内容 Authentication auth = new UsernamePasswordAuthenticationToken(name, password, authorities); return auth; } else { throw new BadCredentialsException("密码错误"); } } else { throw new UsernameNotFoundException("用户不存在"); } } /** * 是否可以提供输入类型的认证服务 * @param authentication * @return */ @Override public boolean supports(Class
authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); }}

权限类型,负责存储权限和角色

package com.jwt.server.provider;import org.springframework.security.core.GrantedAuthority;/** * 权限类型,负责存储权限和角色 * * @author zyl */public class GrantedAuthorityImpl implements GrantedAuthority {    /**     *      */    private static final long serialVersionUID = 1L;        private String authority;    public GrantedAuthorityImpl(String authority) {        this.authority = authority;    }    public void setAuthority(String authority) {        this.authority = authority;    }    @Override    public String getAuthority() {        return this.authority;    }}

 定义数据库service、dao文件

package com.jwt.server.service;import com.jwt.server.domain.UserInfo;/** * 用户service * @author zyl * */public interface UserService {    /**     * 根据用户名查询用户是否存在     * @param username     * @return     */    public UserInfo findByUsername(String username);    /**     * 添加用户     * @param user     * @return     */    public UserInfo save(UserInfo user);}
package com.jwt.server.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.jwt.server.domain.UserInfo;import com.jwt.server.mapper.UserMapper;import com.jwt.server.service.UserService;@Servicepublic class UserServiceImpl implements UserService {    @Autowired    private UserMapper  usermapper;        @Override    public UserInfo findByUsername(String username) {        return usermapper.findByUsername(username);    }    @Override    public UserInfo save(UserInfo user) {        return usermapper.save(user);    }}

修改之前定义的UserDetailServiceImpl文件为:

package com.jwt.server.service.impl;import static java.util.Collections.emptyList;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import com.jwt.server.domain.UserInfo;import com.jwt.server.service.UserService;/** *  * @author zyl * */@Servicepublic class UserDetailServiceImpl implements UserDetailsService {    @Autowired    protected UserService userService;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        UserInfo user = userService.findByUsername(username);        if (user == null) {            throw new UsernameNotFoundException(username);        }        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),                emptyList());    }}

增加IdGenerator id生成类

 

package com.jwt.server.util;import java.net.InetAddress;import java.net.UnknownHostException;import org.apache.commons.lang3.time.DateFormatUtils;import lombok.extern.slf4j.Slf4j;/** * 与snowflake算法区别,返回字符串id,占用更多字节,但直观从id中看出生成时间 * */@Slf4jpublic enum IdGenerator {    /**     * 每个要生成的序号类型对应一个序号     */    USER_TRANSID("1");    private long workerId;   //用ip地址最后几个字节标示    private long datacenterId = 0L; //可配置在properties中,启动时加载,此处默认先写成0    private long sequence = 0L;    private final long twepoch = 1516175710371L;    private final long workerIdBits = 1L;    private final long datacenterIdBits = 2L;    private final long sequenceBits = 3L;    private final long workerIdShift = sequenceBits;    private final long datacenterIdShift = sequenceBits + workerIdBits;    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;    private long sequenceMask = -1L ^ (-1L << sequenceBits); //4095    private long lastTimestamp = -1L;    private String index;        IdGenerator(String ind) {        this.index = ind;        workerId = 0x000000FF & getLastIP();    }    public synchronized String nextId() {        long timestamp = timeGen(); //获取当前毫秒数        //如果服务器时间有问题(时钟后退) 报错。        if (timestamp < lastTimestamp) {            throw new RuntimeException(String.format(                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));        }        //如果上次生成时间和当前时间相同,在同一毫秒内        if (lastTimestamp == timestamp) {            //sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位            sequence = (sequence + 1) & sequenceMask;            //判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0            if (sequence == 0) {                timestamp = tilNextMillis(lastTimestamp); //自旋等待到下一毫秒            }        } else {            sequence = 0L; //如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加        }        lastTimestamp = timestamp;        long suffix =  ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;                String datePrefix = DateFormatUtils.format(timeGen(), "yyyyMMddHHmmss");        return datePrefix +index + suffix;    }        private long tilNextMillis(long lastTimestamp) {        long timestamp = timeGen();        while (timestamp <= lastTimestamp) {            timestamp = timeGen();        }        return timestamp;    }    private long timeGen() {        return System.currentTimeMillis();    }    private byte getLastIP(){        byte lastip = 0;        try{            InetAddress ip = InetAddress.getLocalHost();            byte[] ipByte = ip.getAddress();            lastip = ipByte[ipByte.length - 1];        } catch (UnknownHostException e) {            log.error("UnknownHostException error:{}", e.getMessage());        }        return lastip;    }        public static void main(String[] args) {        IdGenerator id = IdGenerator.USER_TRANSID;        for (int i = 0; i < 1000; i++) {            String serialNo = id.nextId();            System.out.println(serialNo + "===" + serialNo.length());        }    }}

mapper

package com.jwt.server.mapper;import com.jwt.server.domain.UserInfo;public interface UserMapper {    /**     * 根据用户名查询用户是否存在     *      * @param username     * @return     */    public UserInfo findByUsername(String username);    /**     * 添加用户     *      * @param user     * @return     */    public UserInfo save(UserInfo user);}

mapper.xml

INSERT INTO tb_user (id,username,password) VALUES (#{id,jdbcType=VARCHAR},#{username,jdbcType=VARCHAR},#{password,jdbcType=VARCHAR})

mybatis-config.xml

application.yml配置

#公共配置与profiles选择无关 mapperLocations指的路径是src/main/resourcesmybatis:  typeAliasesPackage: com.jwt.server.domain  mapperLocations: classpath:mapper/*.xml---#开发配置spring:  datasource:    driver-class-name: com.mysql.jdbc.Driver    url: jdbc:mysql://localhost:3306/test?prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true    username: root    password: tiger

修改启动类扫描包

package com.jwt.server;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@SpringBootApplication@MapperScan("com.jwt.server.mapper")//public class SpringJwtApplication {    public static void main(String[] args) {        SpringApplication.run(SpringJwtApplication.class, args);    }        @Bean    public BCryptPasswordEncoder bCryptPasswordEncoder() {        return new BCryptPasswordEncoder();    }}

测试:

自定义登录测试:

好了ok啦。

需要注意的是:

在springboot2.0.4版本的时候由于没有默认的passwordencoder,因此需要手动注入。如果不注入会在鉴权的时候报如下错误

 

如果测试会会有如下情况,说明你注入后未给密码加密

并且这里如果没有存储我们登录的信息时,可能也会有个坑,就是密码加密后与原密码做对比会报如下错误

一般情况下我们用加密后,在授权的时候回去对比密码

这个错误就是会在这个地方产生的。解决办法

自定义身份验证类

自行调用,确保密码一致就ok。具体请看源码分析。

 

转载于:https://www.cnblogs.com/haoliyou/p/9635697.html

你可能感兴趣的文章
机器学习--逻辑回归_LR(内附细说极大似然估计,梯度下降法)
查看>>
Centos7禁止或者允许开机启动服务
查看>>
浏览器重绘和回流
查看>>
14-python-函数
查看>>
微信小程序开发之路之组件化
查看>>
BZOJ4602:[SDOI2016]齿轮(并查集)
查看>>
[ZJOI2014]力【FFT】
查看>>
SQL Server 无法连接到服务器。SQL Server 复制需要有实际的服务器名称才能连接到服务器。请指定实际的服务器名称。...
查看>>
CQL
查看>>
HDU Problem—2124 Repair the Wall 【贪心】
查看>>
yii框架AR详解
查看>>
S5PV210_uart stdio移植
查看>>
c++引用
查看>>
wcf 使用介绍
查看>>
Yii PHP 框架分析 (一)
查看>>
[转] 前端性能的几个基础指标
查看>>
Eclipse 全屏插件
查看>>
Scrapy项目步骤
查看>>
python urlretrieve 下载图片
查看>>
基于springboot构建了一版本自动化测试框架,指定被测试环境,执行测试,支持配置化...
查看>>