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

常用框架(三):spring+springMvc+mybatis+maven+shiro+freemarker

2017-07-04 09:40 465 查看
在之前的博文,常用框架(一)中讲述了spring+springMvc+mybatis+maven框架的搭建,而在常用框架(二)中又集成了Redis

这篇文章主要讲述如何集成shiro+freemarker及其简单应用,还是在原有项目上做扩展,

需要回顾的请点链接跳转:http://blog.csdn.net/mynoteblog/article/details/54922775

项目说明:

(1) 使用freemarker模板,在html页面中可以获取attribute参数,替代jsp。

(2) 使用shiro做登录验证,以及权限分配

(3) pom.xml 追加依赖,这里加入了日志框架,源码会分享,这里就不做介绍了:

[html] view
plain copy

<!-- freemarker -->

<dependency>

<groupId>org.freemarker</groupId>

<artifactId>freemarker</artifactId>

<version>2.3.23</version>

</dependency>

<!-- Spring 整合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-ehcache</artifactId>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-freemarker-tags</artifactId>

<version>0.1-SNAPSHOT</version>

<exclusions>

<exclusion>

<artifactId>javax.servlet-api</artifactId>

<groupId>javax.servlet</groupId>

</exclusion>

</exclusions>

</dependency>

<!-- slf4j日志框架 -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.1.3</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>log4j-over-slf4j</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>org.logback-extensions</groupId>

<artifactId>logback-ext-spring</artifactId>

<version>0.1.2</version>

</dependency>

一,整合freemarker,新建Spring-freemarker.xml,配置如下:

[html] view
plain copy

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 配置freeMarker的模板路径 -->

<bean id="freemarkerConfig"

class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">

<!-- 模板页面放置位置 -->

<property name="templateLoaderPaths" value="/WEB-INF/template/" />

<property name="freemarkerSettings">

<props>

<prop key="tag_syntax">auto_detect</prop>

<prop key="template_update_delay">0</prop>

<prop key="default_encoding">UTF-8</prop>

<prop key="output_encoding">UTF-8</prop>

<prop key="locale">zh_CN</prop>

<prop key="number_format">0.##########</prop>

<prop key="boolean_format">true,false</prop>

<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>

<prop key="date_format">yyyy-MM-dd</prop>

<prop key="time_format">HH:mm:ss</prop>

<prop key="classic_compatible">true</prop>

<prop key="whitespace_stripping">true</prop>

<prop key="template_exception_handler">ignore</prop>

</props>

</property>

</beans>

二,在Spring-config.xml中引入以上新建的配置文件,配置如下:

[html] view
plain copy

<import resource="classpath:Spring-freemarker.xml" />

三,修改Spring-servlet.xml,配置freeMarker视图解析器,如下:

[html] view
plain copy

<!-- 配置freeMarker视图解析器 -->

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">

<property name="contentType" value="text/html; charset=UTF-8" />

<property name="suffix" value=".ftl" />

</bean>

四,测试,新建hello.ftl页面,代码如下:

[html] view
plain copy

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

</head>

<body>

[#--从Attribute中获取参数--]

<h1>${msg}</h1>

[#--判断句式--]

<h1>${(1 == 1)?string("yes", "no")}</h1>

[#--集合遍历--]

<ul>

[#list list as item]

<li>${item}</li>

[/#list]

</ui>

[#if msg=='hello']

<h1>say hello</h1>

[#else]

<h1>say goodbye</h1>

[/#if]

</body>

</html>

这里用的了[#if],[#list] 指令,其他freemarker常用指令,大家可以百度学习。

五,新建测试Controller,FreemarkerController,代码如下:

[java] view
plain copy

package com.maven.web.controller;

import java.util.ArrayList;

import java.util.List;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

@Controller

@RequestMapping("/freemarker")

public class FreemarkerController {

@RequestMapping(value="/hello",method=RequestMethod.GET)

public String sayHello(Model model){

String msg = "Hello World";

List<String> list = new ArrayList<String>(3);

list.add("a");

list.add("b");

list.add("c");

model.addAttribute("msg", msg);

model.addAttribute("list", list);

return "hello";

}

}

六,启动tomcat,访问测试:



***************************************************************

单独集成freemarker演示到此结束,下面重点来看shiro集成。

一,添加Spring-shiro.xml配置文件,如下:

[html] view
plain copy

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- Shiro主过滤器的拦截 -->

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

<!-- 权限管理器 -->

<property name="securityManager" ref="securityManager" />

<!-- 登录地址 -->

<property name="loginUrl" value="/template/login.ftl" />

<!-- 登录后跳转到业务页面 -->

<property name="successUrl" value="/template/main.ftl" />

<!-- 错误页面 -->

<property name="unauthorizedUrl" value="/template/error.ftl" />

<!-- 鉴权过滤器 -->

<property name="filters">

<map>

<entry key="logout" value-ref="logoutFilter" />

<entry key="authc" value-ref="myAuthenticationFilter" />

</map>

</property>

<!-- 权限配置 -->

<property name="filterChainDefinitions">

<value>

<!-- 对静态资源不需要进行认证 -->

/static/** = anon

/tags/** = anon

<!-- 登录退出不需要验证 -->

/login/**=anon

/logout = logout

<!-- 对所有url都需要进行认证 -->

/** = authc

</value>

</property>

</bean>

<!-- 配置权限管理器 -->

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<!-- 缓存管理器 -->

<property name="cacheManager" ref="cacheManager" />

<!-- 我们自定义的realm -->

<property name="realm" ref="myRealm" />

</bean>

<!-- 自定义的Realm,若有多个Realm,可使用'realms'属性代替 -->

<bean id="myRealm" class="com.maven.web.shiro.MyRealm" />

<!-- 重写的认证filter-->

<bean id="myAuthenticationFilter" class="com.maven.web.shiro.MyAuthenticationFilter" />

<!-- 使用shiro提供的logout filter -->

<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">

<property name="redirectUrl" value="/template/login.ftl" />

</bean>

<!-- shiro 缓存管理 -->

<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

<!-- Shiro生命周期处理器 -->

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

</beans>

*********************************************************************************************************************

这里需要注意:

默认的shiro authc 认证参数是username+password,我这里重写了认证方法,改为认证phone+password,

如果没有此需要的,去掉上面配置文件中的以下配置即可,采用默认过滤器





同理,其他anon,logout过滤器也可以重写,还可以自定义过滤器,这些大家可以根据需要来实现,不多介绍。

二,在web.xml 中添加shiro核心过滤器,配置如下:

[html] view
plain copy

<!-- shiro过滤器 -->

<filter>

<filter-name>shiroFilter</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

<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>

三,在Spring-config.xml中引入shiro配置文件,配置如下:

[html] view
plain copy

<import resource="classpath:Spring-shiro.xml" />

四,重写自己的Realm授权+认证方法,在此之前,先实现我们需要的认证参数。

(一) 新建MyAuthenticationToken继承UsernamePasswordToken,添加我们需要的phone属性,代码如下:

(如果不需要自定义属性就不需要写这一步了,直接用UsernamePasswordToken即可)

[java] view
plain copy

package com.maven.web.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

/**

* 重新token,对用户名的认证改为手机号

* @author Administrator

*

*/

public class MyAuthenticationToken extends UsernamePasswordToken{

private static final long serialVersionUID = 1425855488092920451L;

/** 手机号 */

private String phone;

public MyAuthenticationToken(String phone, String password, boolean rememberMe, String host) {

super(null, password, rememberMe, host);

this.phone = phone;

}

/** 默认是返回用户名,改写返回手机号 */

@Override

public Object getPrincipal() {

return phone;

}

@Override

public void clear() {

super.clear();

phone = null;

}

public String getPhone() {

return phone;

}

public void setPhone(String phone) {

this.phone = phone;

}

}

(二) 新建MyAuthenticationFilter继承FormAuthenticationFilter,实现用户认证,代码如下:

[java] view
plain copy

package com.maven.web.shiro;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import org.apache.shiro.web.util.WebUtils;

/**

* 重写token认证

* @author Administrator

*

*/

public class MyAuthenticationFilter extends FormAuthenticationFilter{

public static final String DEFAULT_PHONE_PARAM = "phone";

private String phoneParam = DEFAULT_PHONE_PARAM;

public MyAuthenticationFilter(){

setFailureKeyAttribute(DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);

}

@Override

protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {

String phone = getPhone(request);

String password = getPassword(request);

boolean rememberMe = isRememberMe(request);

String host = getHost(request);

return new com.maven.web.shiro.MyAuthenticationToken(phone,password,rememberMe,host);

}

public String getPhoneParam() {

return phoneParam;

}

public void setPhoneParam(String phoneParam) {

this.phoneParam = phoneParam;

}

protected String getPhone(ServletRequest request) {

return WebUtils.getCleanParam(request, getPhoneParam());

}

}

(三) 新建自己的Realm类,MyRealm 继承AuthorizingRealm,代码如下:

[java] view
plain copy

package com.maven.web.shiro;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.DisabledAccountException;

import org.apache.shiro.authc.IncorrectCredentialsException;

import org.apache.shiro.authc.SimpleAuthenticationInfo;

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 com.maven.web.entity.MenuChild;

import com.maven.web.entity.RoleInfo;

import com.maven.web.entity.UserInfo;

import com.maven.web.exception.AuthenticateException;

import com.maven.web.mapper.MenuChildMapper;

import com.maven.web.mapper.RoleInfoMapper;

import com.maven.web.service.impl.UserService;

public class MyRealm extends AuthorizingRealm{

@Resource

private UserService userService;

@Resource

private MenuChildMapper menuChildMapper;

@Resource

private RoleInfoMapper roleInfoMapper;

public MyRealm() {

setAuthenticationTokenClass(AuthenticationToken.class);

}

/**

* 重写认证方法

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

UserInfo userInfo = null;

if(token instanceof MyAuthenticationToken){

MyAuthenticationToken myToken = (MyAuthenticationToken) token;

String phone = myToken.getPhone();

String password = new String(myToken.getPassword());

try {

userInfo = userService.getUserInfoNyPhone(phone,password);

} catch (AuthenticateException e){

throw new IncorrectCredentialsException(e);

}

if (null == userInfo) {

throw new IncorrectCredentialsException("账号或密码错误!");

}

if (!"0".equals(userInfo.getStatus())) {

throw new DisabledAccountException("帐号"+myToken.getPrincipal()+"已失效!");

}

//比对成功则返回authcInfo

AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(userInfo, userInfo.getPassword().toCharArray(),this.getName());

// this.setSession("currentUser", userInfo);

return authcInfo;

}

return null;

}

/**

* 为认证成功的用户授权,然后就可以调用subject.hasRole等方法进行逻辑判断

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//认证通过后会将需要的身份信息放到SimpleAuthenticationInfo中返回,这是是获取到其中的principal对象

Iterator<?> iterator = principals.fromRealm(getName()).iterator();

if (iterator.hasNext()) {

UserInfo userInfo = (UserInfo) iterator.next();

if (null != userInfo) {

List<String> roleList = new ArrayList<String>();

List<String> permissionList = new ArrayList<String>();

//查询用户角色

RoleInfo userRole = roleInfoMapper.selectByUserId(userInfo.getId());

if(null !=userRole){

roleList.add(userRole.getRoleName());

List<MenuChild> roleMenus = menuChildMapper.getMenuChildByRoleId(userRole.getId());

if(null !=roleMenus){

for(MenuChild menu:roleMenus){

permissionList.add(menu.getMenuName());

}

}

}

//为用户添加权限,角色

SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();

simpleAuthorInfo.addRoles(roleList);

simpleAuthorInfo.addStringPermissions(permissionList);

return simpleAuthorInfo;

}

}

return null;

}

/**

* 将一些数据放到ShiroSession中,以便于其它地方使用

* @see 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到

* 也可以直接从subject的身份对象中取

*/

/* private void setSession(Object key, Object value){

Subject currentUser = SecurityUtils.getSubject();

if(null != currentUser){

Session session = currentUser.getSession();

System.out.println("Session默认超时时间为[" + session.getTimeout() + "]毫秒");

if(null != session){

session.setAttribute(key, value);

}

}

}*/

}

*******************************************************************************************************************

这里说明一下:

认证方法,是对用户在登录页面提交的信息做校验,登录成功才会跳转到主页。

授权方法,用户登录成功后可以根据用户信息查找相关角色,权限,存储到授权对象中。

以后就可以通过Java代码,shiro标签等,获取当前用户的角色,权限,进行模块功能分配等等。下面会演示。

五,用户登录

(一) 新建登录页面,login.ftl,代码如下:

[html] view
plain copy

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

</head>

<body>

<form action="/com.maven.web/login/login" method="post" id="form">

<article>手机号:</article>

<input type="text" name="phone"/>

<article>密码:</article>

<input type="password" name="password"/>

<br/>

<br/>

<input type="submit" value="登录" />

<br/>

</form>

<span style="color:red">${msg}</span>

</body>

</html>

(二) 新建 LoginContoller,代码如下:

[java] view
plain copy

package com.maven.web.controller;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.subject.Subject;

import org.slf4j.Logger;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import com.maven.web.entity.UserInfo;

import com.maven.web.shiro.MyAuthenticationToken;

import com.maven.web.util.LoggerUtils;

@Controller

@RequestMapping("/login")

public class LoginController {

private final Logger logger = LoggerUtils.getLogger("LoginController");

/**

* 基于手机号和密码的身份认证

* @param userInfo

* @param model

* @param request

* @return

*/

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

public String checkLogin(UserInfo userInfo,Model model,HttpServletRequest request){

logger.debug("userInfo", userInfo);;

MyAuthenticationToken token = new MyAuthenticationToken(userInfo.getPhone(), userInfo.getPassword(), true, null);

Subject subject = SecurityUtils.getSubject();

try {

subject.login(token);//会到自定义的Realm中进行验证返回

if(subject.isAuthenticated()){

return "redirect:/menu/list";

}else{

token.clear();

}

}catch(AuthenticationException e){

model.addAttribute("msg",e.getMessage());

}

return "login";

}

/**

* 用户登出

*/

@RequestMapping("/logout")

public String logout(){

SecurityUtils.getSubject().logout();

return "login";

}

}

(三) 新建登录成功页面,main.ftl,代码如下:

[html] view
plain copy

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

</head>

<body>

<span>菜单栏</span>

<ul>

[#list menus as menu]

<li><a href="#">${menu.menuParent.menuName}</a></li>

<li>

<ul>

[#list menu.menuChilds as menuChild]

<li><a href=${menuChild.menuUrl}>${menuChild.menuName}</a></li>

[/#list]

</ul>

</li>

[/#list]

</ul>

<span>${msg}</span>

<br/>

<div>

[@shiro.hasRole name="管理员"]

<span>角色是管理员才能看到此信息</span>

[/@shiro.hasRole]

</div>

<a href="/com.maven.web/login/logout">logout</a>

</body>

</html>

(四) 新建MenuController,动态查询菜单权限返回主页面main.ftl,代码如下:

[java] view
plain copy

package com.maven.web.controller;

import java.util.ArrayList;

import java.util.List;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;

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.RequestMethod;

import com.maven.web.entity.MenuChild;

import com.maven.web.entity.MenuParent;

import com.maven.web.mapper.MenuChildMapper;

import com.maven.web.mapper.MenuParentMapper;

import com.maven.web.vo.Menu;

@Controller

@RequestMapping("/menu")

public class MenuController {

@Resource

private MenuParentMapper menuParentMapper;

@Resource

private MenuChildMapper menuChildMapper;

@RequestMapping(value="/list",method=RequestMethod.GET)

public String listMenu(Model model){

List<Menu> menus = new ArrayList<Menu>();

List<MenuParent> parents = menuParentMapper.getMenuParentList();

if(parents!=null && !parents.isEmpty()){

for(MenuParent parent:parents){

Menu menu = new Menu();

menu.setMenuParent(parent);

List<MenuChild> childs = menuChildMapper.getMenuChildByPid(parent.getId());

if(childs!=null && !childs.isEmpty()){

menu.setMenuChilds(childs);

}

menus.add(menu);

}

}

model.addAttribute("menus",menus);

Subject subject = SecurityUtils.getSubject();

if(subject.hasRole("管理员")){

model.addAttribute("msg","这里是管理员私人信息!");

}

return "main";

}

}

六,启动tomcat,测试:

这里先给大家看一下我数据库的用户信息:



启动访问,未登录会跳转到登录页面

测试一:错误用户信息



测试二:新注册用户状态为未激活



测试三:管理员登录



测试四:普通用户登录



*******************************************************************************************************

这里请大家注意,管理员登录成功后可以看到红色框和黄色框两条信息,普通用户登录后看不到。

(1) 红色框信息:

这是因为在MenuController中做了角色判断,注意代码片段:

[java] view
plain copy

Subject subject = SecurityUtils.getSubject();

if(subject.hasRole("管理员")){

model.addAttribute("msg","这里是管理员私人信息!");

}

(2) 黄色框信息:

这是因为在main.ftl页面中使用了shiro标签,注意代码片段:

[html] view
plain copy

<div>

[@shiro.hasRole name="管理员"]

<span>角色是管理员才能看到此信息</span>

[/@shiro.hasRole]

</div>

********************************************************************************************************

接下来说明shiro标签的使用:

(一) 在freemarker中需要先引入shiro-freemarker-tags-0.1-SNAPSHOT.jar,本例已经添加到pom.xml文件中,

大家可以下载源码,在lib中找到jar包添加到本地,也可以到网上下载,下面会贴出链接。

(二) 新建CustomFreeMarkerConfigurer 继承FreeMarkerConfigurer,代码如下:

[java] view
plain copy

package com.maven.web.template;

import java.io.IOException;

import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.jagregory.shiro.freemarker.ShiroTags;

import freemarker.template.TemplateException;

/**

* 编写自己的freemarker配置

* @author Administrator

*

*/

public class CustomFreeMarkerConfigurer extends FreeMarkerConfigurer{

/**

* 重写导入shiro标签

*/

@Override

public void afterPropertiesSet() throws IOException, TemplateException {

super.afterPropertiesSet();

this.getConfiguration().setSharedVariable("shiro", new ShiroTags());

}

}

(三) 修改Spring-freemarker.xml配置文件,改成自定义的CustomFreeMarkerConfigurer



关于shiro标签与freemarker的整合,大家可以跳转链接学习:http://blog.csdn.net/lingyundouer/article/details/50487459

如果使用默认的authc认证,只需要将代码中MyAuthenticationToken替换成UsernamePasswordToken,

然后获取到的phone参数替换成username就可以,其他都是一样的。

以上就介绍完毕了,可能有所遗漏,大家可以下载源码参考:http://pan.baidu.com/s/1dFilb6h
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: