您的位置:首页 > 编程语言 > Java开发

springboot中shiro 的应用

2018-09-09 15:11 134 查看

apache shiro 是一个简单便捷的安全框架,也是官方推荐的安全框架,作用和spring scurity一样,但是却比scurity 轻便,灵活,由于spring boot 中没有整合shiro 所以如果我们要在项目中使用shiro需要自己配置一些东西 接下来开始介绍shiro 的使用

 

千里之行始于足下,先来认识shiro,上面这张图就是shiro官网的介绍图,可以很清楚的看出来 shiro基本功能包括四个方面

1. authentication(权限认证):通常用于用户登录

2. Autoorization(授权):主要就是给角色授权,就是说这个用户能做什么不能做什么

3. Session管理器:知道如何创建session或者如何管理session

4. Cryptography(加密):通过加密算法,对数据加密的同时,保证数据容易使用

当然也支持其他的一些功能(日后再聊)比方说支持缓存,记住密码,web支持,并发,支持单元测试,支持以另一个用户运行(在许可的情况下)

当然咯  shiro,他不会自己维护权限,这里需要我们去设计。接下来认识shiro代码

当我们的程序开始运行的时候,

1#首先会给到 subject,也就是当前应用程序的用户,是一个抽象的概念

2# SecurityManager 一个安全管理器,也是shiro中真正的执行者,管理着subject

3# realm 域 也就是安全的数据源,最简单的应用就是数据库

接下来看看shiro的架构图,当我们把这张图搞懂了,那么shiro 的学习就只剩下了解代码了

这张图不多介绍,基本的上面也都讲过,需要注意的一点就是,我们可以看得出基本这些shiro中的主体都是被security Manager管理着,

这里使用的是maven,贴上使用的dependency

[code]                <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</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>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

启动方式是用的maven的插件启动的,启动的时候要用maven中的springboot启动,如果直接点击运行,程序是无法找到jsp文件,而报404错误。

[code]         <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

 

接下来看代码了,数据库连接使用的是mybaits,所以实体类,mapper文件(放在resource下面),dao接口都是通过mybatis generator自动生成的,就不贴代码 了,不懂mybais逆向工程的请戳  mybaits逆向工程,直接贴一张图,这张图也是这个demo的所有java代码了

接下来就是application.properties配置文件中的内容

[code]  #配置数据库连接
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=Root

#配置mabatis位置
mybatis.mapepr-location=mybatis/*.xml
mybatis.type-aliases-package=com.lj.shiromanagerweb.model
mybatis.config-location=classpath:mybatis-config.xml

#配置springmvc
spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.jsp

 服务端只写了一个方法,

[code]import com.lj.shiromanagerweb.dao.UserMapper;
import com.lj.shiromanagerweb.model.User;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
* @author Eric
* @date create in2018/8/7 21:06
*/
@Service
public class UserService {

@Resource
UserMapper userMapper;

public User findByUserName(String username) {
return userMapper.findByUsername(username);
}

}

 需要说明的一点是因为这个地方查询数据库是夺标联查,所以,在mapper文件的这个方法稍稍有些不一样mapper中的findByuserName代码如下

[code]<select id="findByUsername" parameterType="string" resultMap="userMap">
SELECT u.*, r.*, p.*
FROM user u
INNER JOIN user_role ur on ur.uid = u.uid
INNER JOIN role r on r.rid = ur.rid
INNER JOIN permission_role pr on pr.rid = r.rid
INNER JOIN permission p on pr.pid = p.pid
WHERE u.username = #{username}
</select>

重点是接下来的三个类中的代码

1.自定义realm文件 AuthRealm.java 主要做两件事,一件事就是从数据库查询出相关数据,来验证用户,第二件事就是获取相关权限,给用户授权

[code]import com.lj.shiromanagerweb.model.Permission;
import com.lj.shiromanagerweb.model.Role;
import com.lj.shiromanagerweb.model.User;
import com.lj.shiromanagerweb.service.UserService;
import org.apache.commons.collections.CollectionUtils;
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.*;

/**
* 自定义realm授权部分
*
* @author Eric
* @date create in2018/8/7 21:30
*/
public class AuthRealm extends AuthorizingRealm {

@Autowired
private UserService userService;

/**
* 授权方式
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//通过传进来的principals迭代获取用户
User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
//        String userName = (String) principals.getPrimaryPrincipal();
List<String> permissionList = new ArrayList<>();//
List<String> roleNameList = new ArrayList<>();
//获取登录人的角色,通过相关角色查询权限
Set<Role> roleSet = user.getRoles();
if (CollectionUtils.isNotEmpty(roleSet)) {
for (Role role : roleSet) {
roleNameList.add(role.getRname());
Set<Permission> permissionSet = role.getPermissions();
if (CollectionUtils.isNotEmpty(permissionSet)) {
for (Permission permission : permissionSet) {
permissionList.add(permission.getPname());
}
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionList);
info.addRoles(roleNameList);
return info;
}

/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
User user = userService.findByUserName(username);
return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());

}

}

因为在shiro底层校验密码是通过SimpleCredentialsMatcher,那么这里我们也是实现了SimpleCredentialsMatcher,让校验的方式自己来实现,代码如下:

[code]/**
* 告诉我们密码的校验规则,密码校验规则由我们自己实现
* @author Eric
* @date create in2018/8/7 22:15
*/
public class CredentialMatcher extends SimpleCredentialsMatcher{

@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String  password = new String(usernamePasswordToken.getPassword());
String dbPassword = (String) info.getCredentials();
return this.equals(password, dbPassword);
}
}

接下来就需要写我们shiro中的一些配置,我们在ShiroConfiguration这个类里面注入,这样一来,shiro的校验读取都是按照我们写的方式来操作

[code]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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.LinkedList;

/**
*
* 定制springboot与shiro的关系,让系统在刚开始的时候就读取@Configuration
*
* 当程序开始的时候先读取shiroFilter--->securityManager---authRealm--->credentialMatcher
* @author Eric
* @date create in2018/8/7 22:21
*/
@Configuration
public class ShiroConfiguration {
/**
* anon:匿名访问
* authc;form表单验证
* authBasic:基础的http验证
* logout:注销拦截器
* noSessionCreation:没有session创建session的拦截器
* perms:权限县验证
* roles:角色验证
* @param manager
* @return
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);
//登录页面
bean.setLoginUrl("/login");
//登录成功之后跳转的页面
bean.setSuccessUrl("/index");
//权限认证失败的时候跳转的页面
bean.setUnauthorizedUrl("/unauthorized");
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//前面的url使用后面的过滤器
/*  filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("loginUser", "anon");
filterChainDefinitionMap.put("/**", "user");*/
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}

/**
* 使用我们自己的方式去验证一个用户
* @param authRealm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
return manager;
}

/**
* 使用自定义realm 和自定义的密码校验规则
* @param matcher
* @return
*/
@Bean("authRealm")
public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(matcher);
return authRealm;
}

/**
* 校验密码的规则,这里是由自己定义的
* @return
*/
@Bean("credentialMatcher")
public CredentialMatcher credentialMatcher() {
return new CredentialMatcher();
}

/**
* 让程序使用自定义的securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
//默认值是false
creator.setProxyTargetClass(true);
return creator;
}

}

接下来及时springboot 的main方法   这个方法里面有一些需要注意的,及时天剑@MapperScan,这是mybatis中,让配置文件和dao联系在一起

[code]@SpringBootApplication
@ComponentScan
@MapperScan(basePackages = {"com.lj.shiromanagerweb.dao"})
public class ShiroManagerWebApplication {

public static void main(String[] args) {
SpringApplication.run(ShiroManagerWebApplication.class, args);
}
}

准备工作都已经做好了,那么接下来只剩下前段代码,和controller了,前段代码就省掉了

TestController.java 代码如下

[code]import com.lj.shiromanagerweb.model.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
* @author Eric
* @date create in2018/8/7 22:45
*/
@Controller
@Slf4j
public class TestController {

@RequestMapping("/login")
public String login() {
return "login";

}

@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject!=null) {
subject.logout();
}
return "logout";

}

@RequestMapping("/index")
public String index() {
return "index";
}

@RequestMapping("/loginUser")
public String loginUser(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session, Model model) {
System.out.println("username = " + username);
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();

String username1 = new String(token.getUsername());
String password1 = new String(token.getPassword());
log.info("username1={};password1={}",username1,password1);

try {
subject.login(token);
User user = (User) subject.getPrincipal();
session.setAttribute("user", user);
return "index";
} catch (AuthenticationException e) {
e.printStackTrace();
model.addAttribute("msg", "用户名或者账号错误");
return "login";
}

}

}

这样就大功告成了,,用maven中的springboot插件启动。

 

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: