springboot整合shiro(使用了thymeleaf模板引擎)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

springboot整合shiro(使⽤了thymeleaf模板引擎)数据库结构
1. 项⽬⽬录结构
2. pom.xml 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance" xsi:schemaLocation="/POM/4.0.0 https:///xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.xej</groupId>
<artifactId>springboot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.13</version>
</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. application.yml 配置⽂件
spring:
datasource:
url: jdbc:mysql://localhost:3306/tb_shiro?useUnicode=true&amp&characterEncoding=utf-8&serverTimezone=GMT username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
thymeleaf:
cache: false
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: cn.xej.pojo
configuration:
map-underscore-to-camel-case: true # 该配置就是将带有下划线的表字段映射为驼峰格式的实体类属性4. User 实体类
package cn.xej.pojo;
import lombok.Data;
@Data
public class User {
private String userId;
private String password;
private String name;
}
5. UserDao接⼝
package cn.xej.mapper;
import er;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserDao {
public User findByUserId(String userId);
public List<String> queryRolesIdByUserId(String userId);
}
6. UserDao.xml⽂件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-////DTD Mapper 3.0//EN"
"/dtd/mybatis-3-mapper.dtd">
<mapper namespace="erDao">
<select id="findByUserId" resultType="User">
SELECT *
FROM tb_user
WHERE user_id=#{userId}
</select>
<select id="queryRolesIdByUserId" resultType="String">
SELECT ur.role_id
FROM tb_user AS u,tb_user_role AS ur
WHERE er_id = er_id
AND er_id = #{userId}
</select>
</mapper>
7. UserService接⼝
package cn.xej.service;
import er;
import java.util.List;
public interface UserService {
// 根据⽤户id查询该⽤户 
public User findByUserId(String userId);
// 根据⽤户id获取该⽤户⾓⾊
public List<String> getRolesIdByUserId(String userId);
}
8. UserServiceimpl接⼝实现类
package cn.xej.service.impl;
import erDao;
import er;
import erService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceimpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User findByUserId(String userId) {
return userDao.findByUserId(userId);
}
@Override
public List<String> getRolesIdByUserId(String userId) {
return userDao.queryRolesIdByUserId(userId);
}
}
9. SpringbootShiroApplication 启动类添加包扫描注解
package cn.xej;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cn.xej.mapper") // 配置⼀个或多个包路径,⾃动的扫描这些包路径下的类,⾃动的为它们⽣成代理类。

public class SpringbootShiroApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
}
10. RespObj
package mon;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RespObj {
private Integer code;
private String message;
private Object data;
public static RespObj build(Integer code,String message,Object data){
return new RespObj(code,message,data);
}
}
11. SysController(路由跳转控制器)
package cn.xej.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SysController {
// 进⼊到登录页⾯
@RequestMapping({"/","/welcome"})
public String welcome(){
return "login";
}
// 进⼊到⾸页(登录成功或游客)
@RequestMapping("/index")
public String index(){
return "index";
}
}
12. UserController(控制器)
package cn.xej.controller;
import mon.RespObj;
import er;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import ernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user")
public class UserController {
// 登录⽤户,返回json数据,因此要加 @ResponseBody注解
@PostMapping("/toLogin")
@ResponseBody
public RespObj toLogin(String userId, String password, HttpSession session){
// 1. 获取主体 subject
Subject subject = SecurityUtils.getSubject();
// 2. 将账号和密码进⾏封装
UsernamePasswordToken token = new UsernamePasswordToken(userId,password); // 3. shiro认证,进⼊⾃定义UserRealm中
try {
subject.login(token);
// 认证成功,将⽤户名存到session中,返回json数据
session.setAttribute("currentUserName",((User)subject.getPrincipal()).getName());
return RespObj.build(200,"ok",null);
} catch (Exception e) {
System.out.println("账号或密码错误");
return RespObj.build(500,"账号或密码错误",null);
}
}
// 注销⽤户
@PostMapping("/logout")
@ResponseBody
public RespObj logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return RespObj.build(200,"ok",null);
}
@RequestMapping("/add")
public String add(){
return "pages/add";
}
@RequestMapping("/update")
public String update(){
return "pages/update";
}
}
13. ⾃定义UserRealm
package cn.xej.config;
import er;
import erService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
User user = (User) principalCollection.getPrimaryPrincipal();
List<String> roles = userService.getRolesIdByUserId(user.getUserId());
info.addRoles(roles);
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证");
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
User user = userService.findByUserId(token.getUsername());
if(user==null){
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
// 第⼀个参数,将会传到授权中进⾏获取,⽐如 User user = (User) principalCollection.getPrimaryPrincipal();
// 第⼆个参数,是该⽤户数据库⾥的密码
// 第三个参数,是当前类名
}
}
14. Shiro配置⽂件(ShiroConfig)
package cn.xej.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration // 该注解表⽰该类是配置类
public class ShiroConfig {
// 我的所有⽅法名都是类名的⾸字母⼩写,不然要写成 @Bean(name="xxx") xxx是⾃定义的⽅法名
// 配置⾃定义Realm
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
// 配置安全管理器,把⾃定义的Realm添加到安全管理器中
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
// 配置Filter⼯⼚,设置对应的过滤条件和跳转条件,把安全管理器添加到Filter⼯⼚中
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
shiroFilterFactoryBean.setLoginUrl("/welcome"); //设置进⼊登录页⾯的url
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
shiroFilterFactoryBean.setSuccessUrl("/index"); //设置登录成功url
Map<String,String> map = new LinkedHashMap<String, String>();
// index和 user/toLogin两个路径不⽤拦截
map.put("/index","anon");
map.put("/user/toLogin","anon");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使⽤cglib,防⽌重复代理和可能引起代理出错的问题
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
15. login.html页⾯
<!DOCTYPE html>
<html lang="en" xmlns:th="">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https:///libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<form>
账号 <input type="text" name="userId" id="userId"><br/>
密码 <input type="password" name="password" id="password"><br/>
<button type="button" onclick="login()">登录</button>
</form>
<script>
function login() {
var userId = $('#userId').val();
var password = $('#password').val();
$.ajax({
url: '/user/toLogin',
type: 'post',
data: {
userId: userId,
password: password
},
success: function (data) {
if(data.code===200){
alert('登录成功');
location.href="/index"; // 这⾥还是⾛控制器,让控制器来返回具体页⾯
}
}
})
}
</script>
</body>
</html>
16. index.html (⾸页) (当⽤户没登录时,三元表达式显⽰游客名字,登录时显⽰该⽤户名字,并通过shiro标签执⾏UserRealm中的授权⽅法,然后通过该⽤户的id获取他的⾓⾊)
<!DOCTYPE html>
<html lang="en" xmlns:th="" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https:///libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<p>⾸页</p><br>
<a href="javascript:;" th:text="${session.currentUserName}!=null ? ${session.currentUserName} : '游客'"></a><br>
<a href="welcome">登录</a>
<a href="javascript:;" class="logout">注销</a><br>
<div shiro:hasRole="admin">
<a href="user/add">添加教师</a><br>
<a href="user/update">更新教师</a><br>
</div>
<div shiro:hasAnyRoles="admin,teacher">
<a href="user/add">添加学⽣</a><br>
<a href="user/update">更新学⽣</a><br>
</div>
<div shiro:hasRole="student">
<a href="user/update">普通学⽣</a><br>
</div>
<a href="">游客</a><br>
<script>
$('.logout').click(function () {
$.ajax({
url: '/user/logout',
type: 'post',
success: function (data) {
if(data.code===200){
alert("注销成功");
location.href="/index";
}
}
})
})
</script>
</body>
</html>
17. 实现⽤户密码加密
1. ⾸先在shiro 配置⽂件中添加解密规则,如下⾯的解密凭证器,然后放到UserRealm中
// shiro 解密凭证器
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
// 配置⾃定义Realm
@Bean
public UserRealm userRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return userRealm;
}
2.然后在UserRealm 认证⽅法中,返回4个参数
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证");
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
User user = userService.findByUserId(token.getUsername());
if(user==null){
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(user.getUserId()),getName());
// 第⼀个参数,将会传到授权中进⾏获取,⽐如 User user = (User) principalCollection.getPrimaryPrincipal();
// 第⼆个参数,是该⽤户数据库⾥的密码
// 第三个参数,是salt,这⾥我是⽤⽤户id当作盐
// 第四个参数,是当前类名 
}
3. 最后写个测试⽅法,产⽣密码加密
“123” 是⽤户密码
“teacher1” 是加密的盐
1024 是加密次数
String password1 = new SimpleHash("MD5","123","teacher1",1024).toString(); System.out.println("password1 "+password1);。

相关文档
最新文档