一、先说必要的配置文件:
1、web.xml文件添加上
springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /*
2、application-context.xml配置文件
在application-context配置文件中加入spring security配置文件的引用:
xml version="1.0" encoding="UTF-8"?>> > ......此处省略
id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
......
name="exposeSpringMacroHelpers" value="true"/>
name="contentType" value="text/html;charset=UTF-8">
name="toolboxConfigLocation">
/WEB-INF/toolbox.xml
3、securityConfig.xml文件:
xml version="1.0" encoding="UTF-8"?>xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd"> <security:http auto-config="true" use-expressions="true" access-denied-page="/denied">
<
security
:intercept-url
pattern
="/login"
access
="permitAll"
/><security:intercept-url pattern="/logout" access="permitAll" />
<security:intercept-url pattern="/slr/prd/**" access="hasAnyRole('SHOW_PRD','ADD_PRD','MOD_PRD','DEL_PRD')" />
<security:intercept-url pattern="/slr/order/**" access="hasAnyRole('SHOW_ORD','CONFIRM_ORD','CANCEL_ORD')" />
<security:intercept-url pattern="/slr/mgm/**" access="hasAnyRole('SHOW_ACCT','ADD_ACCT','MOD_ACCT','DEL_ACCT')" />
<security:form-login login-page="/login" default-target-url="/welcome"/>
<security:session-management session-authentication-strategy-ref="concurrentSessionControlStrategy" />
security:http>
id="concurrentSessionControlStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"> name="sessionRegistry" ref="sessionRegistry" /> name="maximumSessions" value="1"> id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" /> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider user-service-ref="userDetailsService"> <security:password-encoder ref="passwordEncoder"/> security:authentication-provider> security:authentication-manager> class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/> id="userDetailsService" class="com.security.UserDetailsServiceImpl" />
4、toolbox.xml文件:
type="string"> version 1.3 ...... sec request com.security.SecurityVelocity
这样在vm界面中就可以直接使用啦~
#if($sec.hasAnyRole("SHOW_PRD","ADD_PRD","MOD_PRD","DEL_PRD"))>商品管理
href="/slr/prd/list">商品管理 #end
二、配置文件中的hasAnyRole()方法和UserDetailService的实现
1.SecurityVelocity.java:
public class SecurityVelocity { public boolean hasAnyRole(String ...roles) { return isRole(roles); } /*** * 前端传入数组参数 * @param viewRole 可变数组 1个或者多个 * @return 是否有权限 */ private boolean isRole(String ...roles){ /** 前端数组为空 */ if(null == roles || roles.length <= 0){ return false; } /** 获取当前用户登录对象 */ UserDetails userDetails = null; try { userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } catch (Exception e) { return false; } if(null == userDetails){ return false; } /** 获取当前用户登录对象所有权限 */ Collection extends GrantedAuthority> authorities = userDetails.getAuthorities(); if(null == authorities){ return false; } boolean flag = false; Iterator extends GrantedAuthority> iter = authorities.iterator(); while (iter.hasNext()) { GrantedAuthority grantedAuthority = iter.next(); /**遍历前端列表的所有角色*/ for (String vr : roles) { if(vr.equals(grantedAuthority.getAuthority())){ flag = true; break; } } if (flag) break; // 已经匹配上角色了则不再需要匹配其它角色。 } return flag; } }
2.UserDetailServiceImpl.java
public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserAuthService userAuthService; @Autowired private UserService userService; public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { User securityUser = null; UserVo mngVo = userService.getSellerByMobile(userName); //权限设置 CollectiongrantedAuthorities = this.getGrantedAuthorities(mngVo); boolean enables = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; securityUser = new User(mngVo.getId().toString(), mngVo.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities); return securityUser; } /** * 根据用户获取该用户拥有的权限 * @param user 当前用户 * @return 用户角色集合 */ private SetgetGrantedAuthorities(SellerVo user) { Set grantedAuthorities = new HashSet() ; if(null != user){ Listauths = user AuthService.getUserRoleListByUserId(user.getId()); if(null != auths && auths.size() > 0 ) { for(AuthorityVo auth : auths) { grantedAuthorities.add(new SimpleGrantedAuthority(auth.getCode()+"")); } } } return grantedAuthorities; } }
这个类主要是在登录时调用来设置用户权限信息的。
三:自己登录逻辑与springsecurity结合
public class LoginController extends BaseController { ...... // show login page @RequestMapping("login") public String index(Model model, HttpServletRequest request, HttpServletResponse response) { // 判断登录状态是否还有效,有效的话自动登录 final UserVo Vo = getLoginUser(request);
if (userVo != null && userVo.getId() != null) { //登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。
Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH); SecurityContextHolder.getContext().setAuthentication(authentication); // redirect to homepage return "redirect:/"; }
//返回登录页 return LOGIN; } // do login action @RequestMapping("do_login") public String doLogin(Model model, HttpServletResponse response, HttpServletRequest request, String mobile, String password) { log.info("User input data: mobile = " + mobile); if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) { model.addAttribute("msg", "请输入手机号和密码!"); return INDEX; } ...... try { UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);
//调用loadUserByUsername设置权限信息
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication); log.info("恭喜用户 " + mobile() + " 登录成功。"); ......
response.sendRedirect('上一次访问的页面或welcome');} catch (IOException ex) {
四、登录页login.vm
html>
lang="en">
charset="UTF-8">
登录页
......
五、注意点:
用spring security默认的登录,登录页的form的action="/manage/j_spring_security_check"这样写,默认会调用UsernamePasswordAuthenticationFilter中的验证信息。
自己写的登录逻辑主要是要把验证信息写入到spring security的上下文中,三行主要的代码如下:
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);
//调用loadUserByUsername设置权限信息
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
对于自动登录的,需要把权限也放入spring security上下文中,但是生成 UsernamePasswordAuthenticationToken对象需要密码的明文,这里我是把它放在缓存中,是自动登录的直接从缓存中取出来放进spring security上下文中。
final UserVo Vo = getLoginUser(request);
if (userVo != null && userVo.getId() != null) {
//登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。
Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH);
SecurityContextHolder.getContext().setAuthentication(authentication);
// redirect to homepage
return "redirect:/";
}
自动登录开始把权限也放入spring security上下文中,结果出现了循环重定向,是因为之前选择了自动登录,则if条件为true,这时重定向的/login,又开始执行登录页面的代码,造成了重定向死循环。
看到别人写的一篇好文,有助于理解:http://blog.csdn.net/zy_cookie/article/details/49535413