Springboot+Shiro+redis整合

1、Shiro是Apache下的一个开源项目,我们称之为Apache Shiro。它是一个很易用与Java项目的的安全框架,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。shiro属于轻量级框架,相对于security简单的多,也没有security那么复杂。所以我这里也是简单介绍一下shiro的使用。

2、非常简单;其基本功能点如下图所示:

Springboot+Shiro+redis整合

Springboot+Shiro+redis整合

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。

3、这里我就简单介绍一下springboot和shiro整合与基本使用。

需要的基础包:pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.winter

HtmlCssJs

0.0.1-SNAPSHOT

war

HtmlCssJs

This is Html Css Js Demo Project

org.springframework.boot

spring-boot-starter-parent

1.5.3.RELEASE

UTF-8

UTF-8

1.8

net.sourceforge.nekohtml

nekohtml

net.sf.json-lib

json-lib

2.4

jdk15

org.apache.shiro

shiro-spring

1.4.0

org.quartz-scheduler

quartz

2.2.1

org.springframework

spring-context-support

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.0

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-aop

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-tomcat

org.apache.tomcat

tomcat-servlet-api

8.0.36

provided

org.springframework.boot

spring-boot-starter-data-jpa

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-starter-mail

mysql

mysql-connector-java

javax.servlet

javax.servlet-api

javax.servlet

jstl

org.apache.tomcat

tomcat-jsp-api

org.apache.tomcat.embed

tomcat-embed-core

org.apache.tomcat.embed

tomcat-embed-jasper

com.fasterxml.jackson.core

jackson-core

com.fasterxml.jackson.core

jackson-databind

com.fasterxml.jackson.datatype

jackson-datatype-joda

com.fasterxml.jackson.module

jackson-module-parameter-names

com.github.pagehelper

pagehelper-spring-boot-starter

1.1.2

com.alibaba

druid-spring-boot-starter

1.1.0

redis.clients

jedis

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.data

spring-data-redis

aspectj

aspectjweaver

1.5.3

org.weixin4j.spring.boot

weixin4j-spring-boot-starter

1.0.0

com.soecode.wx-tools

wx-tools

2.1.4-RELEASE

org.springframework.boot

spring-boot-maven-plugin

基本配置application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/world?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=root

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.initialSize=5

spring.datasource.minIdle=5

spring.datasource.maxActive=20

spring.datasource.maxWait=60000

mybatis.mapper-locations=classpath*:mapper/*Mapper.xml

mybatis.type-aliases-package=com.cwh.springbootMybatis.entity

#thymeleaf模板使用

#关闭thymeleaf缓存

spring.thymeleaf.cache=false

#去掉thymeleaf的严格的模板校验

#spring.thymeleaf.mode=LEGACYHTML5

#Spring Jpa

spring.jpa.properties.hibernate.hbm2ddl.auto=update

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.show-sql= true

# Redis数据库索引(默认为0)

spring.redis.database=0

# Redis服务器地址

spring.redis.host=127.0.0.1

# Redis服务器连接端口

spring.redis.port=6379

# Redis服务器连接密码(默认为空)

spring.redis.password=

# 连接池最大连接数(使用负值表示没有限制)

spring.redis.pool.max-active=8

# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.pool.max-wait=-1

# 连接池中的最大空闲连接

spring.redis.pool.max-idle=8

# 连接池中的最小空闲连接

spring.redis.pool.min-idle=0

# 连接超时时间(毫秒)

spring.redis.timeout=0

OK ,开始我们需要3个实体,用户,角色和权限

// 实体类名

@Entity

// 映射数据库表名

@Table(name="tb_user")

public class User implements Serializable{

/**

*/

private static final long serialVersionUID = 1L;

@Id

// 主键自增长

@GeneratedValue(strategy=GenerationType.AUTO)

public Long id;

// 列名

@Column

public String name;

@Column

public String password;

@OneToMany(cascade=CascadeType.ALL)

private List roles;

// 省略get set..

@Entity

@Table(name="tb_role")

public class Role {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Long id;

private String roleName;

@ManyToOne(fetch = FetchType.EAGER)

private User user;

@OneToMany(cascade = CascadeType.ALL)

private List permissions;

// 省略get set..

@Entity

@Table(name="tb_permission")

public class Permission {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Long id;

private String permission;

@ManyToOne(fetch = FetchType.EAGER)

private Role role;

// 省略get set..

重启服务,对应看数据库,已自动生成如下表:

Springboot+Shiro+redis整合

注入Shiro Factory和SecurityManager:

// shiro核心配置类

@Configuration

public class ShiroConfiguration {

@Bean

public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager){

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 必须设置 SecurityManager

shiroFilterFactoryBean.setSecurityManager(securityManager);

// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射

shiroFilterFactoryBean.setLoginUrl("/nologin");

// 设置无权限时跳转的 url;

shiroFilterFactoryBean.setUnauthorizedUrl("/403");

// 默认成功界面,登陆成功后 跳转

shiroFilterFactoryBean.setSuccessUrl("/index");

// 设置拦截器

Map<string> filterChainDefinitionMap = new LinkedHashMap<>();/<string>

//游客,开发权限

filterChainDefinitionMap.put("/guest/**", "anon");

//用户,需要角色权限 “user”

filterChainDefinitionMap.put("/user/**", "roles[user]");

//管理员,需要角色权限 “admin”

filterChainDefinitionMap.put("/admin/**", "roles[admin]");

//开放登陆接口

filterChainDefinitionMap.put("/login", "anon");

//其余接口一律拦截

//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截

filterChainDefinitionMap.put("/**", "authc");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

System.out.println("Shiro拦截器工厂类注入成功");

return shiroFilterFactoryBean;

}

/**

* 注入 securityManager

*/

@Bean

public org.apache.shiro.mgt.SecurityManager securityManager() {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

// 设置realm.

securityManager.setRealm(customRealm());

return securityManager;

}

/**

* 自定义身份认证 realm;

*

* 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm,

* 否则会影响 CustomRealm类 中其他类的依赖注入

*/

@Bean

public CustomRealm customRealm() {

return new CustomRealm();

}

}

注意:SecurityManager这有点坑,默认是导入java.lang.SecurityManager包 可以写成我这种格式,即使不太美观

org.apache.shiro.mgt.SecurityManager

这里说下:ShiroFilterFactory中已经由Shiro官方实现的过滤器

Springboot+Shiro+redis整合

anon:所有url都都可以匿名访问;

authc: 需要认证才能进行访问;

user:配置记住我或认证通过可以访问

身份认证

在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.

认证实现

public class CustomRealm extends AuthorizingRealm{

private UserRepository userMapper;

@Autowired

private void setUserMapper(UserRepository userMapper) {

this.userMapper = userMapper;

}

/**

* 获取身份验证信息

* Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。

*

* @param authenticationToken 用户身份信息 token

* @return 返回封装了用户信息的 AuthenticationInfo 实例

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("————身份认证方法————");

UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

// 从数据库获取对应用户名密码的用户

User user = userMapper.findPasswordByName(token.getUsername());

if (null == user.getPassword()) {

throw new AccountException("用户名不正确");

} else if (!user.getPassword().equals(new String((char[]) token.getCredentials()))) {

throw new AccountException("密码不正确");

}

return new SimpleAuthenticationInfo(token.getPrincipal(), user.getPassword(), getName());

}

/**

*

* @param principalCollection

* @return

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println("————权限认证————");

String username = (String) SecurityUtils.getSubject().getPrincipal();

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//获得该用户角色

User user = userMapper.findRoleByName(username);

Set set = new HashSet<>();

List roles = user.getRoles();

for (Role role2 : roles) {

String roleName = role2.getRoleName();

//需要将 role 封装到 Set 作为 info.setRoles() 的参数

set.add(roleName);

}

//设置该用户拥有的角色

info.setRoles(set);

return info;

}

}

Springboot+Shiro+redis整合

然后在控制器层;

@Controller

public class UserController {

@Autowired

private IUserService userService;

// 注:此处不能使用@ResponseBody 因为使用过后,控制层就当字符串返回了

// 创建Model对象,将信息以Key,Value形式放置Model域中

@RequestMapping("/findOne")

public String findOne(Long id,Model model){

User user = userService.findOne(id);

model.addAttribute("user", user);

return "/user/findOne";

}

@RequestMapping("/nologin")

public String nologin(){

return "/user/login";

}

/**

* 登陆

*

* @param username 用户名

* @param password 密码

*/

@RequestMapping(value = "/login", method = RequestMethod.POST)

@ResponseBody

public String login(String username, String password) {

// 从SecurityUtils里边创建一个 subject

Subject subject = SecurityUtils.getSubject();

// 在认证提交前准备 token(令牌)

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

// 执行认证登陆

subject.login(token);

// 根据权限,指定返回数据

List role = userService.findRoleByName(username).getRoles();

for (Role role2 : role) {

if ("user".equals(role2.getRoleName())) {

return "欢迎登陆";

}

if ("admin".equals(role2.getRoleName())) {

return "欢迎来到管理员页面";

}

}

return "权限错误!";

}

@RequestMapping("/index")

public String index(Model model){

model.addAttribute("msg", "欢迎访问主页");

return "/user/index";

}

}

省掉了登陆界面等

进行访问,我们直接在地址栏访问主页。

192.168.40.88:8080/index会跳转到登陆界面,因为我们还未登陆

Springboot+Shiro+redis整合

登陆后,

Springboot+Shiro+redis整合


分享到:


相關文章: