springboot+swagger+shiro+ssm的前后分离

springboot+swagger+shiro+ssm的前后分离

思路:用户登录的认证逻辑我们自己写,用户请求资源的认证交给shiro

用户认证登录的逻辑: 首先前端传过来用户名和密码,通过用户名查询用户,如果查询到,比较密码,如果一致,则生成一个token(这个token作为用户唯一的身份标识)返回给前端,并将这个token保存在数据库中

用户请求资源的逻辑

首先,自定义一个过滤器,使用login方法,这里的login传参仅仅是我们的token,因此,我们要重写token,调用login方法以后-----realm 我们将前端传过来的token值封装并返回(源码中认证最后一步比较的是密码,我们换成比较token),然后重写HashedCredentialsMatcher 中的doCredentialsMatch方法,从数据库中获取到token值,两个进行比较,如果相等,则认证通过,否则,认证失败

1.导包

<dependencies>
		<!--导入支持json的包-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.68</version>
		</dependency>

		<!--导入shiro相关的包-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>

		<!--导入swagger相关的包-->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>

		<!--导入druid-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.20</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.2</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

2.编写全局配置

mybatis.type-aliases-package=com.wcc.springboot.dao
mybatis.mapper-locations=classpath:mapping/*.xml

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.password=root
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/mydb_01?\
  useUnicode=true&useJDBCCompliantTimezoneShift=true&\
  useLegacyDatetimeCode=false&serverTimezone=UTC

3. 编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable{

    private static final long serialVersionUID = -491147926030118870L;
    private Integer id;
    private String name;
    private String password;
    private String token;
    private Date expiredData;//token的过期时间
}

4. 编写自定义异常

@Data
@NoArgsConstructor
public class BaseException extends RuntimeException {
    private int code;
    private String msg;

    public BaseException(int code,String msg){
        super(msg);
        this.code=code;
        this.msg=msg;
    }

    public String getMsg(){
        return msg;
    }

    public int getCode(){
        return code;
    }
}

5.编写token

public class CustomToken extends UsernamePasswordToken {
    private String token;

    public CustomToken(String token){
        this.token=token;
    }

    @Override
    public Object getPrincipal () {
        return token;
    }
}

6.编写realm

public class CustomRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo ( PrincipalCollection principalCollection ) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo ( AuthenticationToken authenticationToken ) throws AuthenticationException {
       CustomToken customToken =(CustomToken)authenticationToken;
        String token = (String) customToken.getPrincipal();
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(token, token, getName());
        return authenticationInfo;
    }
}

7.编写认证器

public class CustomCredential extends HashedCredentialsMatcher {
    @Autowired
    private UserService userService;
    @Override
    public boolean doCredentialsMatch ( AuthenticationToken token, AuthenticationInfo info ) {
        CustomToken customToken = (CustomToken)token;
//        获取前端传递过来的token
        String frontToken = (String) customToken.getPrincipal();
//        看数据库中是否存在这个token,如果存在,则返回true
        boolean b = false;

       try {
           b = userService.isExsit(frontToken);
       }catch (BaseException e){
           throw  new BaseException(30004,"查询失败");
       }
       if (!b){
           throw new BaseException(30005,"授权信息无效");
       }
        return true;
    }
}

8. 编写过滤器

public class CustomFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed ( ServletRequest servletRequest, ServletResponse servletResponse, Object o ) throws Exception {
        return false;
    }
// 过滤器,每一次身份校验的时候,都会执行到这里
    @Override
    protected boolean onAccessDenied ( ServletRequest servletRequest, ServletResponse servletResponse ) throws Exception {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        try {
//        校验身份,首先获取前端传过来的token
//        将前端的token名设置在一个常量类中
            String frontToken = request.getHeader(Constant.RE_TOKEN);
//        判断token是否为空,如果为空,说明用户身份是不合法的
            if (StringUtils.isEmpty(frontToken)){
                throw new BaseException(40001,"用户请求的token不能为空");
            }else{
    //            将token进行封装,封装完成之后,交给shiro进行验证,看身份认证是否合法
                CustomToken customToken = new CustomToken(frontToken);
    //         下面这个方法是用户登陆成功以后,在访问其他资源的时候,做身份校验的
    //            这个方法在用户第一次登录的时候,并不会执行
                getSubject(servletRequest,servletResponse).login(customToken);
            }
        } catch (BaseException e) {
            resultResponse(e.getCode(),e.getMsg(),servletResponse);
            return false;
        } catch (AuthenticationException e) {// 校验没通过的异常
            if(e.getCause() instanceof BaseException){
                BaseException err = (BaseException)e.getCause();
                resultResponse(err.getCode(),err.getMsg(),servletResponse);
            }else{ //说明是shiro的异常
                resultResponse(30001,"用户校验失败",servletResponse);
            }
        }catch (AuthorizationException e){//校验权限
            if (e.getCause() instanceof BaseException){
                BaseException err = (BaseException)e.getCause();
                resultResponse(err.getCode(),err.getMsg(),servletResponse);
            }else{
                resultResponse(30002,"用户没有权限",servletResponse);
            }
        }catch (Exception e){//表示的一些未考虑的异常
            if (e.getCause() instanceof BaseException){
                BaseException err = (BaseException)e.getCause();
                resultResponse(err.getCode(),err.getMsg(),servletResponse);
            }else{
                resultResponse(30003,"系统异常",servletResponse);
            }
        }
        return true;
    }

    /**
     * 此方法返回的是异常的结果,告诉前端的一些消息
     */

    public void resultResponse(int code,String msg,ServletResponse response){
//        构建返回的数据
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code",code);
        jsonObject.put("msg",msg);
//        设置返回的数据类型
        response.setContentType(String.valueOf(MediaType.APPLICATION_JSON_UTF8));
//        获取输出流
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(jsonObject.toJSONString().getBytes());
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

9. 编写配置文件

9.1 全局配置

@SpringBootConfiguration
@ComponentScan(basePackages = {"com.wcc.springboot"})
@MapperScan(basePackages = {"com.wcc.springboot.mapper"})
public class AppConfig {
}

9.2 swagger配置

@SpringBootConfiguration
@EnableSwagger2
public class SwaeggerConfig {

    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wcc.springboot.controller"))
                .paths(PathSelectors.any())
                .build();

    }

    public ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .version("V1.0")
                .title("shiro+swagger+前后分离")
                .description("这是shiro整合swagger的前后分离测试")
                .contact("laowang")
                .build();
    }
}

9.3 shiro配置

@SpringBootConfiguration
public class ShiroConfig {


//    配置过滤器
    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean shiroFilterFactoryBean =
                new ShiroFilterFactoryBean();
//        配置自己的过滤器
        Map<String,Filter> map = new LinkedHashMap<>();
        map.put("token",new CustomFilter());
        shiroFilterFactoryBean.setFilters(map);

//        配置对请求拦截和过滤的设置
        Map<String,String> map1 = new LinkedHashMap<>();
//        登陆页面不拦截
        map1.put("/toLogin","anon");
//        swagger的所有请求资源和请求地址都不拦截
        map1.put("/swagger/**","anon");
        map1.put("/v2/api-docs","anon");
        map1.put("/swagger-ui.html","anon");
        map1.put("/swagger-resources/**","anon");
        map1.put("/webjars/**","anon");
        map1.put("/favicon.ico","anon");
        map1.put("/captcha.jpg","anon");
        map1.put("/csrf","anon");
//        设置我们自己的校验
        map1.put("/**","token,authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map1);
        shiroFilterFactoryBean.setSecurityManager(securityManager());
       return shiroFilterFactoryBean;
    }

//    配置安全管理器
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =
                new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm());
        return securityManager;
    }

//    配置我们的realm
    @Bean
    public CustomRealm customRealm(){
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(customCredential());
        return customRealm;
    }
//    配置凭证匹配器
    @Bean
    public CustomCredential customCredential(){
        CustomCredential customCredential = new CustomCredential();
        return customCredential;
    }

//    开启基于注解的权限校验
    @Bean
    public AuthorizationAttributeSourceAdvisor sourceAdvisor(){
        AuthorizationAttributeSourceAdvisor advisor =
                new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator proxyCreator(){
        DefaultAdvisorAutoProxyCreator proxyCreator = new
                DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }
}

10. 编写常量类

public class Constant {
    public static final String RE_TOKEN="token";
}

11. 编写mapper

public interface UserMapper {
   User findUserByName( String name);

    void  updateToken( User userByName );

    User findToken( String frontToken );

    List<User> findAll();
}

12. 编写mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wcc.springboot.mapper.UserMapper">


    <select id="findUserByName" resultType="com.wcc.springboot.dao.User">
    select * from user WHERE name=#{name};
    </select>
    <update id="updateToken">
    UPDATE user SET  token=#{token} WHERE id=#{id}
    </update>
    <select id="findToken" resultType="com.wcc.springboot.dao.User">
    select * from user WHERE token = #{frontToken} ;
    </select>
    <select id="findAll" resultType="com.wcc.springboot.dao.User">
    SELECT * FROM user;
    </select>
</mapper>

13. 编写service

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User findByName ( String name ) {
        User user = userMapper.findUserByName(name);
        return user;
    }

    @Override
    public User login ( User user ) {
        String name = user.getName();
        User userByName = userMapper.findUserByName(name);
        if (userByName==null){
            throw new BaseException(40000,"用户名不存在");
        }
        if (!userByName.getPassword().equals(user.getPassword())){
                throw  new BaseException(40001,"密码错误");
            }
                String token = UUID.randomUUID().toString();
                Date date = new Date();
                userByName.setToken(token);
                userByName.setExpiredData(date);
//            设置token和过期时间以后,更新数据库
                updateToken(userByName);
//            将密码设置为空,然后将用户对象返回给前端
                userByName.setPassword("");

        return userByName;
    }

    public void updateToken ( User userByName ) {
        userMapper.updateToken(userByName);
    }

    @Override
    public boolean isExsit ( String frontToken ) {
      User user = userMapper.findToken(frontToken);
        if (user==null){
            return false;
        }
        return true;
    }

    @Override
    public List<User> findAll () {
     List<User> users = userMapper.findAll();
     return users;
    }
}

14. 编写controller

@RestController
@Api(tags = {"usercontroller测试"})
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "toLogin",method = RequestMethod.POST)
    @ApiOperation(value = "用户登录的接口测试")
    public DataResult<User> toLogin( User user){
        DataResult<User> dataResult = null;
        try {
            User user1 = userService.login(user);
            dataResult = new DataResult<User>(
                    BaseCodeAndMsg.SUCCESS.getCode(),
                    BaseCodeAndMsg.SUCCESS.getMsg(),
                    user1
            );
        } catch (Exception e) {
           if (e instanceof  BaseException){
               BaseException error = (BaseException) e;
               dataResult =  new DataResult<User>(error.getCode(),error.getMsg(),null);
           }else {
               dataResult = new
                       DataResult<User>(BaseCodeAndMsg.SYSTEEM_ERROR.getCode(),
                       BaseCodeAndMsg.SYSTEEM_ERROR.getMsg(),null);
           }
           return dataResult;
        }

        return dataResult;
    }

    @RequestMapping(value = "getAll",method = RequestMethod.POST)
    @ApiOperation(value = "获取所有用户信息的接口")
    @ApiImplicitParam(paramType = "header",name="token",required = true,dataType = "String")
    public DataResult<List<User>> getAllUser(){
        DataResult<List<User>> dataResult;
        try {
            List<User> users = userService.findAll();
            dataResult = new DataResult<List<User>>(BaseCodeAndMsg.SUCCESS.getCode(),
                    BaseCodeAndMsg.SUCCESS.getMsg(),users);
        } catch (Exception e) {
            dataResult = new DataResult<List<User>>(BaseCodeAndMsg.SYSTEEM_ERROR.getCode(),
                    BaseCodeAndMsg.SYSTEEM_ERROR.getMsg(),null);
        }
        return dataResult;
    }
}

15.编写封装结果集

15.1 定义结果集的接口

public interface CodeAndMsg {
    int getCode();
    String getMsg();
}

15.2 定义枚举实现接口

public enum BaseCodeAndMsg implements CodeAndMsg {
    SUCCESS(0,"操作成功"),
    SYSTEEM_ERROR(50001,"系统异常"),
    INVALIDATE_ERROR(40003,"数据校验异常"),
    TOKEN_ERROR(40001,"用户校验异常"),
    DATA_ERROR(40000,"数据传入异常"),
    AUTHORIZATION_REEOR(40004,"用户权限不够,无法访问")
    ;
    private int code;
    private String msg;
    BaseCodeAndMsg(int code,String msg){
        this.code = code;
        this.msg = msg;
    }
    @Override
    public int getCode () {
        return code;
    }

    @Override
    public String getMsg () {
        return msg;
    }
}

15.3 定义返回给前端的结果集

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DataResult<T> {
    private Integer code;
    private String msg;
    private T data;
    public DataResult(CodeAndMsg codeAndMsg){
        this.code = codeAndMsg.getCode();
        this.msg = codeAndMsg.getMsg();
    }

    public DataResult(CodeAndMsg codeAndMsg,T data){
        this.code = codeAndMsg.getCode();
        this.msg = codeAndMsg.getMsg();
        this.data = data;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值