springboot整合shiro(含MD5加密)

springboot整合shiro(含MD5加密)

开发环境:

1、mysql - 5.7.21

2、navicat(mysql客户端管理工具)

3、idea 2017

4、jdk9

5、tomcat 8.5

6、springboot

7、mybatis 3

8、shiro

9、maven

项目开始:

一、数据库设计:

注:数据库三张表和spring整合shiro中的一模一样,在那边已经详细说明,这里直接大家看下三张表的ER图。
springboot整合shiro(含MD5加密)_第1张图片
二、添加依赖,配置mybatis

1、用idea新建Spring Initializr项目,项目结构如下:

springboot整合shiro(含MD5加密)_第2张图片

2、添加依赖:

org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.2 mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-tomcat provided org.springframework.boot spring-boot-starter-test test org.apache.shiro shiro-core 1.2.3 org.apache.shiro shiro-spring 1.2.3 com.alibaba druid 1.0.20 org.apache.commons commons-lang3 3.4 org.springframework spring-context-support 4.1.7.RELEASE org.apache.tomcat.embed tomcat-embed-jasper javax.servlet jstl javax.servlet javax.servlet-api 3、application.properties

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///#
spring.datasource.username=#
spring.datasource.password=#

mybatis

mybatis.mapper-locations=mappers/*.xml
mybatis.type-aliases-package=com.zhu.shiro.entity

视图解析器

spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.jsp
三、项目设计

注:spring整合shiro中是只有User实体类,在UserDao中定义了三个方法,通过表的关键关系查询Role和Permission;这里将采用另一种方式,三个实体类,设置实体类的关联关系。
springboot整合shiro(含MD5加密)_第3张图片
1、entity层

User.java

public class User {
private Integer uid;
private String username;
private String password;
private Set roles = new HashSet<>();
}
Role.java

public class Role {
private Integer rid;
private String name;
private Set permissions = new HashSet<>();
}
Permission.java

public class Permission {
private Integer pid;
private String name;
}
2、dao层

UserDao.java

public interface UserDao {
User findByUsername(String username);
}
UserDao.xml

SELECT * FROM tb_user u,tb_role r,tb_permission p WHERE u.rid=r.rid AND p.rid=r.rid AND u.user_name=#{username} 3、service层

@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User findByUsername(String username) {
return userDao.findByUsername(username);
}
}
4、junit测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void findByUsername() {
User u = userService.findByUsername(“tom”);
Set roleSet = u.getRoles();
for (Role role : roleSet){
Set permissionSet = role.getPermissions();
for (Permission permission : permissionSet){
System.out.println(permission.getName());
}
System.out.println(role.getName());
}
}
}
运行结果:

springboot整合shiro(含MD5加密)_第4张图片

数据库中tom是admin角色,有增删改查权限,符合预期,测试通过。5、controller层

TestController.java

@Controller
public class TestController {
//用户登录
@RequestMapping("/loginUser")
public String loginUser(@RequestParam(“username”) String username,
@RequestParam(“password”) String password,
HttpSession session) {
//把前端输入的username和password封装为token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
session.setAttribute(“user”, subject.getPrincipal());
return “index”;
} catch (Exception e) {
return “login”;
}
}
//退出登录
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return “login”;
}
//访问login时跳到login.jsp
@RequestMapping("/login")
public String login() {
return “login”;
}
//admin角色才能访问
@RequestMapping("/admin")
@ResponseBody
public String admin() {
return “admin success”;
}
//有delete权限才能访问
@RequestMapping("/edit")
@ResponseBody
public String edit() {
return “edit success”;
}
@RequestMapping("/test")
@ResponseBody
@RequiresRoles(“guest”)
public String test(){
return “test success”;
}
}
说明:这里用户登录方法用到了shiro,但是这里还没配置shiro,所以暂时不能使用,先搭起整个骨架,然后再加入shiro。

6、jsp页面

login.jsp

(登录页面)

%@ page contentType=“text/html;charset=UTF-8” language=“java” %>

Login

欢迎登录!



index.jsp

(登录成功跳转的页面)

<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>

Title

欢迎登录,${user.username}

unauthorized.jsp

(无权访问跳转的页面)

<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>

unauthorized unauthorized! 现在说一下要求:

admin路由要求只有具有admin角色的用户才能访问,edit路由需要有delete权限的用户才能访问,test路由要guest角色才能访问,login、loginUser都不做拦截,本文讲解两种拦截方式,对test的拦截是在controller对应的方法上加注解,其他是拦截是写在shiro的配置类中。

预期分析:

tom是有admin角色和所有权限,所以用tom登录后,可以访问edit和admin,但是不能访问guest;而cat是guest角色,只有create和query权限,所以不能访问admin和edit,但是可以访问guest。

四、配置shiro

由于springboot还没有集成shiro,所以不能直接在application.properties中配置,需要通过类的方式配置。

核心配置类:

ShiroConfiguration.java

@Configuration
public class ShiroConfiguration {
/**

  • 密码校验规则HashedCredentialsMatcher
  • 这个类是为了对密码进行编码的 ,
  • 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
  • 这个类也负责对form里输入的密码进行编码
  • 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
    /
    @Bean(“hashedCredentialsMatcher”)
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    //指定加密方式为MD5
    credentialsMatcher.setHashAlgorithmName(“MD5”);
    //加密次数
    credentialsMatcher.setHashIterations(1024);
    credentialsMatcher.setStoredCredentialsHexEncoded(true);
    return credentialsMatcher;
    }
    @Bean(“authRealm”)
    @DependsOn(“lifecycleBeanPostProcessor”)//可选
    public AuthRealm authRealm(@Qualifier(“hashedCredentialsMatcher”) HashedCredentialsMatcher matcher) {
    AuthRealm authRealm = new AuthRealm();
    authRealm.setAuthorizationCachingEnabled(false);
    authRealm.setCredentialsMatcher(matcher);
    return authRealm;
    }
    /
    *
  • 定义安全管理器securityManager,注入自定义的realm
  • @param authRealm
  • @return
    /
    @Bean(“securityManager”)
    public SecurityManager securityManager(@Qualifier(“authRealm”) AuthRealm authRealm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    manager.setRealm(authRealm);
    return manager;
    }
    /
    *
  • 定义shiroFilter过滤器并注入securityManager
  • @param manager
  • @return
    */
    @Bean(“shiroFilter”)
    public ShiroFilterFactoryBean shiroFilter(@Qualifier(“securityManager”) SecurityManager manager) {
    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    //设置securityManager
    bean.setSecurityManager(manager);
    //设置登录页面
    //可以写路由也可以写jsp页面的访问路径
    bean.setLoginUrl("/login");
    //设置登录成功跳转的页面
    bean.setSuccessUrl("/pages/index.jsp");
    //设置未授权跳转的页面
    bean.setUnauthorizedUrl("/pages/unauthorized.jsp");
    //定义过滤器
    LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/index", “authc”);
    filterChainDefinitionMap.put("/login", “anon”);
    filterChainDefinitionMap.put("/loginUser", “anon”);
    filterChainDefinitionMap.put("/admin", “roles[admin]”);
    filterChainDefinitionMap.put("/edit", “perms[delete]”);
    filterChainDefinitionMap.put("/druid/", “anon”);
    //需要登录访问的资源 , 一般将/放在最下边
    filterChainDefinitionMap.put("/
    ", “authc”);
    bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return bean;
    }
    /
  • Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理 .
  • @return
    /
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
    creator.setProxyTargetClass(true);
    return creator;
    }
    /
    *
  • 配置shiro跟spring的关联
  • @param securityManager
  • @return
    /
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier(“securityManager”) SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
    }
    /
    *
  • lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
  • (可选)
    */
    @Bean(“lifecycleBeanPostProcessor”)
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
    }
    }
    注:这个类每个bean的作用都已在代码中注释说明,这个类就相当于spring整合shiro的spring-shiro.xml中对shiro的配置。

自定义realm:

AutuRealm.java

public class AuthRealm extends AuthorizingRealm{
@Autowired
private UserService userService;
/**

  • 为用户授权
  • @param principals
  • @return
    /
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //获取前端输入的用户信息,封装为User对象
    User userweb = (User) principals.getPrimaryPrincipal();
    //获取前端输入的用户名
    String username = userweb.getUsername();
    //根据前端输入的用户名查询数据库中对应的记录
    User user = userService.findByUsername(username);
    //如果数据库中有该用户名对应的记录,就进行授权操作
    if (user != null){
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //因为addRoles和addStringPermissions方法需要的参数类型是Collection
    //所以先创建两个collection集合
    Collection rolesCollection = new HashSet();
    Collection perStringCollection = new HashSet();
    //获取user的Role的set集合
    Set roles = user.getRoles();
    //遍历集合
    for (Role role : roles){
    //将每一个role的name装进collection集合
    rolesCollection.add(role.getName());
    //获取每一个Role的permission的set集合
    Set permissionSet = role.getPermissions();
    //遍历集合
    for (Permission permission : permissionSet){
    //将每一个permission的name装进collection集合
    perStringCollection.add(permission.getName());
    }
    //为用户授权
    info.addStringPermissions(perStringCollection);
    }
    //为用户授予角色
    info.addRoles(rolesCollection);
    return info;
    }else{
    return null;
    }
    }
    /
    *
  • 认证登录
  • @param token
  • @return
  • @throws AuthenticationException
    */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    //token携带了用户信息
    UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
    //获取前端输入的用户名
    String userName = usernamePasswordToken.getUsername();
    //根据用户名查询数据库中对应的记录
    User user = userService.findByUsername(userName);
    //当前realm对象的name
    String realmName = getName();
    //盐值
    ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());
    //封装用户信息,构建AuthenticationInfo对象并返回
    AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
    credentialsSalt, realmName);
    return authcInfo;
    }
    }
    注:这个类也有详细的注释说明。

这样就完成了springboot对shiro的整合,接下来就可以进行测试了!

五、测试

tom登录
springboot整合shiro(含MD5加密)_第5张图片

springboot整合shiro(含MD5加密)_第6张图片

tom访问admin

springboot整合shiro(含MD5加密)_第7张图片

tom访问test
springboot整合shiro(含MD5加密)_第8张图片

cat登录

springboot整合shiro(含MD5加密)_第9张图片

cat访问admin
springboot整合shiro(含MD5加密)_第10张图片

cat访问test

springboot整合shiro(含MD5加密)_第11张图片

测试结果与预期相符,测试通过,springboot整合shiro成功!

特别说明:

由于设置了MD5加密,所以数据库中存储的用户密码应该是加密后的密文,否则在登录页面输入明文会验证不通过。假如1234的密文为asdfghjkl,数据库中存储的应该是asdfghjkl,在登录时输入1234就能验证通过。

附上明文转密文的代码:

public static void main(String[] args) {
String hashAlgorithName = “MD5”;
String password = “登录时输入的密码”;
int hashIterations = 1024;//加密次数
ByteSource credentialsSalt = ByteSource.Util.bytes(“登录时输入的用户名”);
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
若不使用MD5加密

1、添加一个类

public class CredenttiaMatcher 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);
}
}
2、将ShiroConfiguration.java中名为"hashedCredentialsMatcher"的bean替换成:

*@Bean(“credenttiaMatcher”)
public CredenttiaMatcher credenttiaMatcher() {
return new CredenttiaMatcher();
}
将名为"authRealm"的bean替换成:

@Bean(“authRealm”)
@DependsOn(“lifecycleBeanPostProcessor”)//可选
public AuthRealm authRealm(@Qualifier(“credenttiaMatcher”) CredenttiaMatcher matcher) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(matcher);
return authRealm;
}
3、AuthRealm.java中的doGetAuthenticationInfo方法里面的内容替换成:

//=未加密版==
//token携带了用户登录的信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
String username = usernamePasswordToken.getUsername();
//根据前端输入的用户名查询数据库中的记录
User user = userService.findByUsername(username);
//校验密码,验证登录
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
完成以上3步就去掉了MD5加密。

以上内容属于个人学习笔记整理,如有错误,欢迎批评指正!

你可能感兴趣的:(springboot)