7、ssm整合shiro

来源:互联网 发布:大数据世界txt下载 编辑:程序博客网 时间:2024/04/18 14:31

1、每个管理系统都需要登陆,那么,安全和权限的问题随之而来,主流的是shiro和spring security两种框架实现,今天我们先以shiro为例来做一个简单的登陆验证、以及权限控制的实例吧!

2、项目架构(ssm搭建步骤详情看我前面的博客)

image

3、实现的效果

image

4、首先是引入依赖:

<shiro.version>1.3.1</shiro.version><!--shiro--><dependency>  <groupId>org.apache.shiro</groupId>  <artifactId>shiro-core</artifactId>  <version>${shiro.version}</version></dependency><dependency>  <groupId>org.apache.shiro</groupId>  <artifactId>shiro-web</artifactId>  <version>${shiro.version}</version></dependency><dependency>  <groupId>org.apache.shiro</groupId>  <artifactId>shiro-spring</artifactId>  <version>${shiro.version}</version></dependency>

5、web.xml中增加shiro过滤器:

<!--shiro权限验证--><filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><async-supported>true</async-supported><init-param>  <param-name>targetFilterLifecycle</param-name>  <param-value>true</param-value></init-param></filter><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

6、spring-shiro.xml文件:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns="http://www.springframework.org/schema/beans"       xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">    <!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录 -->    <bean id="myRealm" class="com.oursnail.login.security.MyRealm">        <property name="credentialsMatcher" ref="passwordMatcher"/>    </bean>    <!--自定义密码校验-->    <bean id="passwordMatcher" class="com.oursnail.login.security.CustomCredentialsMatcher"/>    <!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session -->    <!-- 即<property name="sessionMode" value="native"/>,详细说明见官方文档 -->    <!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">        <property name="realm" ref="myRealm"/>    </bean>    <!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->    <!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 -->    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <!-- Shiro的核心安全接口,这个属性是必须的 -->        <property name="securityManager" ref="securityManager"/>        <!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->        <property name="loginUrl" value="/login"/>        <!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) -->        <!-- <property name="successUrl" value="/system/main"/> -->        <!-- 用户访问未对其授权的资源时,所显示的连接 -->        <!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp -->        <!--<property name="unauthorizedUrl" value="/user/denied"/>-->        <!-- Shiro连接约束配置,即过滤链的定义 -->        <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 -->        <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->        <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 -->        <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->        <property name="filterChainDefinitions">            <value>                /static/**=anon                <!--anon 表示匿名访问,不需要认证以及授权-->                /login/**=anon                <!--其他所有的都需要权限才能访问-->                /**=authc            </value>        </property>    </bean>    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>    <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 -->    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">        <property name="exceptionMappings">            <props>                <prop key="org.apache.shiro.authz.UnauthorizedException">/denied</prop>            </props>        </property>    </bean></beans>

这段代码的主要作用是:制定自定义的Realm用于身份的验证以及密码的校验。下面可以设置不需要授权的路径等,我这里主要设置静态资源和login都不需要认证,其他的都需要认证才能访问。

7、spring-mvc.xml中增加一条配置:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">    <property name="proxyTargetClass" value="true" /></bean><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">    <property name="securityManager" ref="securityManager"/></bean>

光在spring-shiro.xml配置lifecycle还不够,还需要再spring-mvc中配置这段话,否则不起错用。

8、这样,shiro就配置好了,下面新建一张权限表和一张角色表:

CREATE TABLE `t_role` (`id`  int(11) NOT NULL AUTO_INCREMENT ,`role_name`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,PRIMARY KEY (`id`));CREATE TABLE `t_permission` (`id`  int(11) NOT NULL AUTO_INCREMENT ,`permission_name`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,`role_id`  int(11) NULL DEFAULT NULL ,PRIMARY KEY (`id`))

9、用户表

CREATE TABLE `t_user` (`id`  int(11) NOT NULL AUTO_INCREMENT ,`username`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,`password`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,`role_id`  int(11) NULL DEFAULT 1 ,PRIMARY KEY (`id`))

10、下面就是先写一个login.jsp页面:

配置一个路径跳转到login.jsp:

@RequestMapping("")public String login(){    return "login/login";}
<div style="text-align: left;border-color: grey;">    <h3>登陆</h3>    <form action="/login/doLogin" method="post">        username:<input name="username" /><br><br>        password:<input type="password" name="password" /><br><br>        <input type="submit" value="submit"/>    </form></div>

提交到的controller对应处理是:

@RequestMapping(value = "/doLogin",method = RequestMethod.POST)    public String doLogin(@RequestParam String username, @RequestParam String password, Model model,HttpSession session){        Subject subject = SecurityUtils.getSubject();        UsernamePasswordToken token = new UsernamePasswordToken(username, password);        token.setRememberMe(true);        try {            subject.login(token);            User loginUser = (User)session.getAttribute("loginUser");            model.addAttribute("loginUser",loginUser);            return  "/login/index";        }catch (Exception e){            return "/login/login";        }    }

11、执行login方法时就会到realm中验证:

public class MyRealm extends AuthorizingRealm {    @Autowired    private UserService userService;    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {        String username = principalCollection.getPrimaryPrincipal().toString() ;        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo() ;        Set<String> roleNames = userService.findRoles(username) ;        Set<String> permissions = userService.findPermissions(username) ;        info.setRoles(roleNames);        info.setStringPermissions(permissions);        return info;    }    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;        String username = usernamePasswordToken.getUsername();        System.out.println("======="+username);        User user = userService.getUserByName(username);        if(user ==  null){            return null;        }else {            AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),getName());            SecurityUtils.getSubject().getSession().setAttribute("loginUser",user);            return info;        }    }}

先执行 doGetAuthenticationInfo方法实现用户信息的验证。这个先判断用户名正确不正确,然后下面CustomCredentialsMatcher.java中判断密码:

public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {    @Override    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {        try {            UsernamePasswordToken usertoken = (UsernamePasswordToken) token;            String password = String.valueOf(usertoken.getPassword());            Object tokenCredentials = MD5Util.encryptPassword(password);            Object accountCredentials =getCredentials(info);            return equals(tokenCredentials,accountCredentials);        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        return false;    }}

密码匹配和加密的工具类:

public class MD5Util {    /*MD5加密方法*/    public static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {        MessageDigest md5 = MessageDigest.getInstance("MD5");        //防止加密字符串乱码情况        BASE64Encoder base64Encoder = new BASE64Encoder();        String result = base64Encoder.encode(md5.digest(password.getBytes("utf-8")));        return result;    }    /*判断密码是否相等*/    public  static boolean  checkPassword(String inputPwd,String dbPwd) throws UnsupportedEncodingException, NoSuchAlgorithmException {        String result = encryptPassword(inputPwd);        if(result.equals(dbPwd)){            return true;        }else {            return false;        }    }    public static void main(String[] args) throws Exception{        String result = MD5Util.encryptPassword("123");        System.out.println("result:"+result);    }}

一致的话,就不会抛任何异常,controll层顺利跳转。

12、进去之后,如果根据不同的角色和赋予这个角色的权限访问不同的资源呢?

实现就是有依靠realm中的doGetAuthorizationInfo 方法判断,在这个方法里,拿到用户相应的角色和权限的集合,至于集合信息怎么拿呢?

UserMapper.Java中:

Set<String> findRoles(String username);Set<String> findPermissions(String username);

UserMapper.xml:

<select id="findRoles" parameterType="String" resultType="String">select r.role_name from t_user u,t_role r where u.role_id=r.id and u.username=#{username}</select><select id="findPermissions" parameterType="String" resultType="String">select p.permission_name from t_user u,t_role r,t_permission pwhere u.role_id=r.id and p.role_id=r.id and u.username=#{username}</select>

拿到之后,我们就知道这个用户是什么角色,有什么权限了,在这个简单的系统里,swg用户角色是user,权限是user:;hh用户角色是admin,权限是admin:;我们通过注解的方式可以实现权限的控制:

比如我规定user/user这条路径是需要认证的,然后user和admin用户都可以访问;但是user/admin这个路径只能是admin用户访问,如果user用户访问了,就显示拒绝访问的提示。

@RequestMapping("/user") public String user(){    return  "/user/user";}@RequiresRoles("admin")@RequestMapping("/admin")public String admin(){     return "/user/admin";}

一个简单的注解就可以实现角色的控制了。上面提到,如果没有权限,应该给一个提示页面,这种方式变焦友好,在spring-shiro.xml中的配置,提到如果是没有权限的话,就到denied这个页面,我们直接在viesw目录下新建一个denied.jsp页面即可,它会自动跳转。

13、源码下载

14、我依据曾经的这些笔记整理而得:
我的有道云整理笔记1
我的有道云整理笔记2
我的有道云整理笔记3
可以花半天学习一下视频,讲的很好。获取密码是3oqs