第一步:导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--下面导入数据库的使用的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--使用Druid这个连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<!--下面要导入Swagger2的相关的包-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
第二步:封装token
public class CustomToken extends UsernamePasswordToken {
private String token; //用户身份唯一的标识
//这个token是在认证通过之后 用户访问其他资源的时候 来进行你给身份识别的
public CustomToken(String token){
this.token=token;
}
@Override
public Object getPrincipal() {
//在用户认证通过之后 再访问这个方法 默认返回的是什么?
// Realm校验的第一个参数
//校验我们自己写了 还有没有第一个参数这个说法?
return token;
}
}
public class CustomAccessControllerFilter 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
String token=request.getHeader(Constant.REQ_TOKEN);
//第二步:看下这个token是否否为""
if(StringUtils.isEmpty(token)){ //说明你娃身份是非法的
throw new BusinessException(400001,"用户的请求的token不能为空");
}else{ //说明用户带了token
//逻辑
//这里要将token进行封装 封装完了 交给shiro去做认证 看下身份是否合法
CustomToken customToken = new CustomToken(token);
//记住下面这个类 在用户第一次登陆的时候 并不会执行
// 这个执行的时候 是在认证成功之后访问其他资源的
//的时候 机械能给你身份校验的
getSubject(servletRequest,servletResponse).login(customToken);
}
} catch (BusinessException e) {
//如果是这个异常:返回JSON告诉前端出现问题了
resultResponse(e.getMessageCode(),e.getDefaultMesaage(),servletResponse);
return false;
} catch (AuthenticationException e) { //校验没通过的异常
// e.getCause() :返回的是当前异常的实例
if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常
//将异常的实例进行转换
BusinessException err= (BusinessException) e.getCause();
resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse);
}else{ //如果执行到这里 说明 这个异常是shiro返回的
resultResponse(400001,"用户的认证是失败的",servletResponse);
}
return false;
}catch (AuthorizationException e){
// e.getCause() :返回的是当前异常的实例
if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常
//将异常的实例进行转换
BusinessException err= (BusinessException) e.getCause();
resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse);
}else{ //如果执行到这里 说明 这个异常是shiro返回的
resultResponse(403001,"用户没有访问权限",servletResponse);
}
return false;
}catch (Exception e){ //这个分支就捕获一些未考虑的异常了
// e.getCause() :返回的是当前异常的实例
if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常
//将异常的实例进行转换
BusinessException err= (BusinessException) e.getCause();
resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse);
}else{ //如果执行到这里 说明 这个异常是shiro返回的
resultResponse(500001,"系统出现了异常",servletResponse);
}
return false;
}
//当前的方法返回true才放行 否则这个程序也就执行到这里了....
return true;
}
/**
* 这个方法的主要功能就是告诉前端 一些出错的信息
* @param messageCode
* @param defaultMesaage
* @param response
*/
private void resultResponse(int messageCode, String defaultMesaage, ServletResponse response) {
//构建返回的数据
JSONObject jsonObject = new JSONObject();
jsonObject.put("code",messageCode);
jsonObject.put("msg",defaultMesaage);
//设置下返回的数据类型
response.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
//获取输出流
try {
ServletOutputStream out = response.getOutputStream();
//接下来项数据写出去
out.write(jsonObject.toJSONString().getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class CustomRealm extends AuthorizingRealm {
/**
* 授权的方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
return simpleAuthorizationInfo;
}
/**
* 认证的方法
* 将前端放进去的token 取出来 放到校验的SimpleAuthenticationInfo中去 方便后面进行校验
* token放到哪里呢?
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//取出前端传递过来的token
CustomToken customToken= (CustomToken) authenticationToken;
String token= (String) customToken.getPrincipal();
//这里就可以取出这个token
//在这里要将前端传递过来的token给封装到 SimpleAuthenticationInfo 对象中去
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token,token,getName());
return simpleAuthenticationInfo;
}
}
@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.qf.shiro.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 页面信息的配置
* @return
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("springboot+shiro+swagger测试")
.description("这里是整合shiro和Swagger实现前后分离")
.version("v1.0")
.build();
}
}
public enum BaseResponseCode implements ResponseCodeInterface{
/**
* 接下来就要和前端约束好所有的码值以及含义
*/
SUCCESS(0,"操作成功"),
SYSTEM_ERROR(5000001,"系统错误"),
METHOD_INVALIDATE(4000001,"数据校验出错"),
DATA_ERROR(4000002,"传入数据异常"),
TOKEN_NOT_NULL(4010001,"用户认证异常");
//整个构造函数
BaseResponseCode(int code,String msg){
this.code=code;
this.msg=msg;
}
private int code;
private String msg;
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
public class DataResult <T> {
private int code; //返回的码值
private String msg; //返回的错误信息提示
private T data; //返回的数据
//下面这一块是对构造器进行封装
public DataResult(int code,T data){
this.code=code;
this.data=data;
this.msg=null;
}
public DataResult(int code,String msg,T data){
this.code=code;
this.data=data;
this.msg=msg;
}
public DataResult(int code,String msg){
this.code=code;
this.msg=msg;
}
public DataResult(){
this.code=BaseResponseCode.SUCCESS.getCode();
this.msg=BaseResponseCode.SUCCESS.getMsg();
this.data=null;
}
public DataResult(T data){
this.code=BaseResponseCode.SUCCESS.getCode();
this.msg=BaseResponseCode.SUCCESS.getMsg();
this.data=data;
}
public DataResult(ResponseCodeInterface responseCodeInterface){
this.data=null;
this.code=responseCodeInterface.getCode();
this.msg=responseCodeInterface.getMsg();
}
public DataResult(ResponseCodeInterface responseCodeInterface,T data){
this.data=data;
this.code=responseCodeInterface.getCode();
this.msg=responseCodeInterface.getMsg();
}
/**
* 这个很牛逼
* 不带数据的返回
* @param <T>
* @return
*/
public static <T>DataResult success(){
return new DataResult();
}
/**
* 带数据的返回
* @param data
* @param <T>
* @return
*/
public static <T>DataResult success(T data){
return new DataResult(data);
}
/**
* 自己给参数的问题
* @param code
* @param msg
* @param data
* @param <T>
* @return
*/
public static <T>DataResult getResult(int code,String msg,T data){
return new <T>DataResult(code,msg,data);
}
/**
* 自己给参数的问题
* @param code
* @param msg
* @param <T>
* @return
*/
public static <T>DataResult getResult(int code,String msg){
return new <T>DataResult(code,msg);
}
/**
* 直接传递一个枚举进来
* @param baseResponseCode
* @param <T>
* @return
*/
public static <T>DataResult getResult(BaseResponseCode baseResponseCode){
return new <T>DataResult(baseResponseCode);
}
/**
* 直接传递一个枚举进来
* @param baseResponseCode
* @param <T>
* @return
*/
public static <T>DataResult getResult(BaseResponseCode baseResponseCode,T data){
return new <T>DataResult(baseResponseCode,data);
}
}
public class UserController {
private IUserService userService;
private Logger logger= LoggerFactory.getLogger(UserController.class);
/**
* 登陆的接口
* @param user
* @return
*/
public DataResult<User> login(
//这个里面应该干什么?
/**
* 说白了 调用业务逻辑层的方法
* 异常的捕获
* 返回数据
*/
DataResult<User> dataResult=null;
try {
User user1 = userService.login(user);
dataResult=DataResult.success(user1);
} catch (Exception e) {
if(e instanceof BusinessException){ //说明是业务异常
BusinessException err= (BusinessException) e;
//应该干什么?
dataResult=new DataResult<>(err.getMessageCode(),err.getDefaultMesaage());
}else{
//dataResult=new DataResult<>(500001,"系统异常造成登陆失败");
dataResult=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg());
}
return dataResult;
}
return dataResult;
}
/**
* 查找所有的用户数据
* @return
*/
public DataResult<List<User>> findUserList(){
//定义返回数据
DataResult<List<User>> userLists;
try{
//返回用户数据
List<User> users = userService.findUserList();
userLists=DataResult.success(users);
logger.info("获取数据成功....");
}catch (Exception err){
//说明获取信息失败了
logger.error("获取用户信息失败:"+err.fillInStackTrace());
userLists=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg());
}
return userLists;
}
}
11、Service的编写
11.1、Service接口的编写
public interface IUserService {
/**
* 登陆
* @param user
* @return
*/
User login(User user);
/**
* 通过名字找到用户
* @param userName
* @return
*/
User findUserByName(String userName);
/**
* 更新token到数据库
* @param user
*/
void updateToken(User user);
/**
* 查询所有的用户
* @return
*/
List<User> findUserList()throws Exception;
/**
* 判定这个token是否存在
* @param token
* @return
*/
boolean tokenExistsOrNot(String token);
}
public class UserService implements IUserService {
private UserMapper userMapper;
public User login(User user){
//这个类里面应该干什么?
/**第一步:获取到前端传递过来的用户名
*第二步:通过用户名 获取用户对象
* 第三步:校验
* 第四步:生成token保存到数据库
* 第五步:将token封装到返回数据里面给前端
*/
//获取用户名
String userName = user.getUserName();
//通过用户名 找用户名找对象
User userResult = findUserByName(userName);
//第三步:校验
if(null==userResult){ //说明用户名不对
throw new BusinessException(40001,"用户名不对");
}
//说明:用户名是对的
//比较密码
if(!(userResult.getPassword().equals(user.getPassword()))){
throw new BusinessException(40002,"密码不对");
}
//执行到这里说明用户身份合法的
//先将数据保存到一个类里面
//首先要生成token这个值
String token= UUID.randomUUID().toString();
Date date=new Date();
//设置这个值给user对象
userResult.setToken(token);
userResult.setExpireDate(date);
//下面就是更新这个数据库的数据
updateToken(userResult);
//将这个信息返回给前端
//一般情况下 密码是不需要返回的
userResult.setPassword("");
//设置返回数据的对象
//DataResult<User> userDataResult = new DataResult<>(0,"认证成功",userResult);
return userResult;
}
public User findUserByName(String userName) {
return userMapper.findUserByName(userName);
}
public void updateToken(User user) {
userMapper.updateToken(user);
}
public List<User> findUserList() throws Exception{
List<User> userList = userMapper.findUserList();
//接下来对数据进行封装
DataResult<List<User>> dataResult = new DataResult<>(0, "请求完美", userList);
return userList;
}
public boolean tokenExistsOrNot(String token) {
//通过token查询用户信息
User userResult = userMapper.findUserByToken(token);
//接下来就要判断了
if(null!=userResult){
return true;
}
return false;
}
}
public interface UserMapper {
/**
* 通过名字找到用户
* @param userName
* @return
*/
User findUserByName(String userName);
/**
* 更新token到数据库
* @param user
*/
void updateToken(User user);
/**
* 查询所有的用户
* @return
*/
List<User> findUserList();
/**
* 查看当前的token是否在数据库中存在
* @param token
* @return
*/
User findUserByToken(String token);
}
<!--要注意的第二点-->
<mapper namespace="com.qf.shiro.mapper.UserMapper">
<!--通过用户名找用户-->
<select id="findUserByName" parameterType="string" resultType="user">
select * from t_user where userName=#{value}
</select>
<!--更新数据库用户的token-->
<update id="updateToken" parameterType="user">
update t_user set token=#{token} where id=#{id}
</update>
<!--查找所有的用户-->
<select id="findUserList" resultType="user">
select * from t_user
</select>
<!--查看token是否存在-->
<select id="findUserByToken" parameterType="String" resultType="user">
select * from t_user where token=#{value}
</select>
</mapper>
public class User implements Serializable {
private static final long serialVersionUID = -5199670739008077879L;
private Integer id;
private String userName;
private String password;
private String token;
private Date expireDate; //token的过期时间
}