SpringBoot+Shiro之简单的登陆(认证和授权)

SpringBoot+Shiro之简单的登陆(认证和授权)

Scroll Down

SpringBoot+mybatis模块

1、实体类

public class CLogin {
    private Integer id;

    private String username;

    private String userid;

    private String password;

    public CLogin() {
        super();
    }

    public CLogin(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public CLogin(String username, String userid, String password) {
        this.userid = userid;
        this.username = username;
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return “CLogin{“ + “id=” + id + “, username=’” + username + ‘\’’ + “, userid=’” + userid + ‘\’’ + “, password=’” + password + ‘\’’ + ‘}’;
    }
}

2、mapper

import com.shiro.bean.CLogin;

public interface CLoginMapper {
package com.shiro.mapper;
import com.shiro.bean.CLogin;

public interface CLoginMapper {
    /通过账号查询用户信息*/
    CLogin FindUserLogin(String username);
}

3、service

import com.shiro.bean.CLogin;
import com.shiro.mapper.CLoginMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
package com.shiro.service;
import com.shiro.bean.CLogin;
import com.shiro.mapper.CLoginMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CLoginService {
    @Autowired
    CLoginMapper cLoginMapper;
    /通过账号查询用户信息/
    public CLogin FindUserLogin(String username){
        return cLoginMapper.FindUserLogin(username);
    }
}

4、controller

其中用户提交的密码进行了MD5的加密,因为数据库中的密码是MD5加密过的。

import com.shiro.util.TOMD5;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class WellComController {
    @RequestMapping(“/“)
    public String wellcom(Model model){
        model.addAttribute(“index”,”首页Hello world”);
        return “/index”;
    }
    /**
      跳转到添加用户信息
      /
    @RequestMapping(“/user/add”)
    public String add(){
        return “/user/add”;
    }
    /
      跳转到更新用户信息
      */
    @RequestMapping(“/user/update”)
    public String update(){
        return “/user/update”;
    }
    /
      跳转到登陆界面
      /
    @RequestMapping(“/tologin”)
    public String tologin(){
        return “/login”;
    }
    /**
      登陆+shiro处理逻辑
      /
    @RequestMapping(“/login”)
    public String login(String name,String password,Model model){
        /
          使用Shiro编写认证操作
          1、获取Subject
               Subject subject= SecurityUtils.getSubject();
          2、封装用户数据
               UsernamePasswordToken token=new UsernamePasswordToken(name,password);
          3、执行登陆方法
               subject.login(token);
          /
        Subject subject= SecurityUtils.getSubject();

        //这里使用import org.apache.shiro.crypto.hash.Md5Hash; shiro自带的MD5加密密码(因为数据库中的密码是md5的),下面也有java代码将字符串进行md5加密代码

        //这里加密其实有很多方法如下:

        //使用MD5加密算法加密
        //Md5Hash md5=new Md5Hash(“xxx”);
        //System.out.println(“zyy233215===”+md5.toString());
        //加盐
        //md5=new Md5Hash(“xxx”,”sxt”);
        //System.out.println(“zyy233215===”+md5.toString());
        //迭代
        //md5=new Md5Hash(“xxx”,”sxt”,2);
        //System.out.println(“zyy233215===”+md5.toString());

        UsernamePasswordToken token=new UsernamePasswordToken(name,new Md5Hash(password).toString());

        try {
            subject.login(token);
            //登陆成功
            return “redirect:/“;
        }catch (UnknownAccountException e){
            //登陆失败:用户名不存在
            model.addAttribute(“msg”,”用户名不存在”);
            return “login”;
        }catch (IncorrectCredentialsException e){
            model.addAttribute(“msg”,”密码错误”);
            return “login”;
        }
    }
}

MD5加密方法

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class TOMD5 {
    public static String Md5(String sourceStr) {
        String result = “”;
        try {
            MessageDigest md = MessageDigest.getInstance(“MD5”);
            md.update(sourceStr.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer(“”);
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append(“0”);
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
            //System.out.println(“MD5(“ + sourceStr + “,32) = “ + result);
            //System.out.println(“MD5(“ + sourceStr + “,16) = “ + buf.toString().substring(8, 24));
        } catch (NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        return result;
    }
}

5、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"&gt;
<mapper namespace=”com.shiro.mapper.CLoginMapper”>
    <select id=”FindUserLogin” resultType=”com.shiro.bean.CLogin” parameterType=”java.lang.String”>
        select  from c_login where username=#{username}
    </select>
</mapper>

6、application

@MapperScan(basePackages = "com.shiro.mapper") 加上mapper扫描注解

Shiro部分

1、jar包

<!—shiro 与Spring 依赖 —>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

2、ShiroConfig


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    /
      创建ShiroFilterFactoryBean
      /
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(“securityManager”)DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /**添加Shiro内置过滤器,可以实现权限相关的拦截器
          常用的过滤器
               anon:无需认证(登陆)即可访问
               authc:必须认证才可以访问 (登陆)
               user:如果使用rememberMe的功能可以直接访问
               perms:该资源必须得到资源权限才可以访问
               role:该资源必须得到角色权限才可以访问
         /
        Map<String,String> filterMap=new LinkedHashMap<>();
        /
          添加拦截的资源路径,以及权限
          默认拦截后跳转到login.jsp
          /
        / 1、通过url设置user目录下的网页需要授权访问
        filterMap.put(“/add”,”authc”);
        filterMap.put(“/update”,”authc”);/
        /**
          2、方式二 将url前面都添加上user,这样就可以使用下面的方法进行拦截 /
         filterMap.put(“/user/“,”authc”);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        //修改默认的拦截后跳转的登陆界面
        shiroFilterFactoryBean.setLoginUrl(“/tologin”);
        return shiroFilterFactoryBean;
    }
    /**
      创建DefaultWebSecurityManager
      /
    @Bean(name = “securityManager”)
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier(“userRealm”)UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    /
      创建Realm
      /
    @Bean(name = “userRealm”)
    public UserRealm getRealm(){
        return new UserRealm();
    }
}

3、Realm

import com.shiro.bean.CLogin;
import com.shiro.service.CLoginService;
import org.apache.shiro.authc.;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

/
  自动与i的Realm
  /

public class UserRealm extends AuthorizingRealm {
    @Autowired
    CLoginService cLoginService;

    /**
      执行授权逻辑
      /
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println(“执行授权逻辑”);
        return null;
    }
    /
      执行认证逻辑
      /
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //编写shiro判断逻辑,判断用户名和密码
        //1、判断用户名
        UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
        CLogin cLogin=cLoginService.FindUserLogin(token.getUsername());
        if(cLogin==null){
            //用户名不存在
            return null;//shiro底层抛出UnknownAccountException
        }
        //2、判断密码 三个参数:1、返回给subject.login(token);方法的参数  2、数据库中的密码 3、shiro的名字
        return new SimpleAuthenticationInfo(cLogin,cLogin.getPassword(),””);

    }
}

简单的前台:

index.html

<!DOCTYPE html>
<html xmlns:th=”http://www.thymeleaf.org“ lang=”zh”>
<head>
    <meta charset=”UTF-8”/>
    <title>Title</title>
</head>
<body>
    <h1 th:text=”${index}”></h1>
    <hr>
    进入用户添加功能:<a th:href=”@{/user/add}” target=”_blank”>进入用户添加功能</a><br>
    进入用户更新功能:<a th:href=”@{/user/update}” target=”_blank”>进入用户添加功能</a>
</body>
</html>

templates/user目录下的add.html和update.html

<!DOCTYPE html>
<html lang=”zh”>
<head>
    <meta charset=”UTF-8”>
    <title>用户的添加界面</title>
</head>
<body>
    <h1>用户的添加界面</h1>
</body>
</html>
<!DOCTYPE html>
<html lang=”zh”>
<head>
    <meta charset=”UTF-8”>
    <title>用户的更新界面</title>
</head>
<body>
    <h1>用户的更新界面</h1>
</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th=”http://www.thymeleaf.org“ lang=”zh”>
<head>
    <meta charset=”UTF-8”/>
    <title>登陆</title>
</head>
<body>
    <h1>登陆界面</h1>
    <h3 style=”color: red” th:text=”${msg}”></h3>
<form th:action=”@{/login}” method=”post”>
    用户名:<input type=”text” name=”name”><br>
    密  码:<input type=”password” name=”password”><br>
    <button type=”submit” value=”提交”>提交</button>
</form>
</body>
</html>

applocation.yml

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/xxx?characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.jdbc.Driver
    #指定数据源
    type: com.alibaba.druid.pool.DruidDataSource

    # 数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,’wall’用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

  thymeleaf:
    suffix: .html
    prefix:
      classpath: /templates/
    cache: false

  jackson: #返回的日期字段的格式
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    serialization:
      write-dates-as-timestamps: false # true 使用时间戳显示时间

#配置文件式开发
mybatis:
  #全局配置文件的位置
  config-location: classpath:mybatis/mybatis-config.xml
  #所有sql映射配置文件的位置
  mapper-locations: classpath:mybatis/mapper/.xml

以上内容是认证的部分,

下面是授权的部分,某个用户拥有哪个权限?

1、给资源添加授权拦截器 ShiroConfig添加如下

filterMap.put(“/user/add”,”perms[user:add]”);
filterMap.put(“/user/update”,”perms[user:update]”);
//设置未授权的界面
shiroFilterFactoryBean.setUnauthorizedUrl(“/unauth”);

2、controller添加

/
跳转到未授权界面
*/
@RequestMapping(“/unauth”)
public String unauth(){
	return "/unauth";
}

3、未授权界面

<!DOCTYPE html>
<html xmlns:th=”http://www.thymeleaf.org“ lang=”zh”>
<head>
    <meta charset=”UTF-8”>
    <title>未授权</title>
</head>
<body>
    亲,你未经授权访问界面
</body>
</html>

4、UserRealm添加

/
 执行授权逻辑
 只要访问加上授权的资源都会调用改方法
 /
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  System.out.println(“执行授权逻辑”);
  //给资源进行授权
  SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
  //添加授权字符串 这个字符串和加上权限的字符串一致
  //info.addStringPermission(“user:add”);
  //到数据库查询当前用户的授权的字符串
  Subject subject= SecurityUtils.getSubject();
  CLogin cLogin= (CLogin) subject.getPrincipal();//这里的CLogin对象是登陆的时候传过来的
  CLogin dbclogin=cLoginService.FindUserLogin(cLogin.getUsername());
  info.addStringPermission(dbclogin.getPerms());
  return info;
}

5、数据库修改,不同用户不同的权限

6、运行后发现不同用户拥有不同的权限

继续修改:

下面使用thymeleaf 对shiro扩展坐标完成用户没有的功能不显示url点击的链接

1、导入依赖

<dependency>
     <groupId>com.github.theborakompanioni</groupId>
      <artifactId>thymeleaf-extras-shiro</artifactId>
      <version>2.0.0</version>
</dependency>

2、在ShiroConfig中配置ShiroDialect

@Bean
public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
}

3、修改界面,用户对于没有权限的资源不显示可以点击链接

<h1 th:text=”${index}”></h1>
<hr>
<div shiro:hasPermission=”user:add”>
    进入用户添加功能:<a th:href=”@{/user/add}” target=”_blank”>进入用户添加功能</a>
</div>
<div shiro:hasPermission=”user:update”>
    进入用户更新功能:<a th:href=”@{/user/update}” target=”_blank”>进入用户添加功能</a>
</div>
<a th:href=”@{/tologin}”>登陆</a>