我們?cè)诰帉慦eb應(yīng)用時(shí),經(jīng)常需要對(duì)頁(yè)面做一些安全控制,比如:對(duì)于沒(méi)有訪問(wèn)權(quán)限的用戶需要轉(zhuǎn)到登錄表單頁(yè)面。要實(shí)現(xiàn)訪問(wèn)控制的方法多種多樣,可以通過(guò)Aop、攔截器實(shí)現(xiàn),也可以通過(guò)框架實(shí)現(xiàn)(如:Apache Shiro、Spring Security)。
本文將具體介紹在Spring Boot中如何使用Spring Security進(jìn)行安全控制。
整體框架: spring boot spring data jpa spring security
心得
在整理當(dāng)前框架時(shí),遇到了幾個(gè)問(wèn)題
<sec:authorize access="hasRole('ROLE_USER')">
這里是角色ROLE_USER可以看到
</sec:authorize>
<sec:authorize url="/admin">
這里是具有 /admin 資源的用戶可以看到
</sec:authorize>
當(dāng)時(shí)官網(wǎng)是這樣描述著兩個(gè)標(biāo)簽的
此標(biāo)記用于確定是否應(yīng)評(píng)估其內(nèi)容。在spring 3.0,它可以以兩種方式使用 。第一種方法使用了網(wǎng)絡(luò)的安全性表達(dá),在指定access標(biāo)簽的屬性。表達(dá)式求值將被委托給SecurityExpressionHandler<FilterInvocation>應(yīng)用程序上下文中定義(你應(yīng)該在你的基于Web的表達(dá)<http>空間配置,以確保該服務(wù)可用)。所以,例如,你可能有<sec:authorize access="hasRole('ROLE_USER')"></sec:authorize>這這種標(biāo)簽可以直接使用 .
但是對(duì)于 URL 來(lái)講就沒(méi)那么簡(jiǎn)單了.需要自定義DefaultWebInvocationPrivilegeEvaluator類. 下面我會(huì)給出詳細(xì)設(shè)計(jì)代碼,在這之前我想多說(shuō)一句,當(dāng)時(shí)擴(kuò)展的時(shí)候我遇到了標(biāo)簽不起作用,百度 谷歌了好久,也沒(méi)有解決問(wèn)題.我在群里問(wèn)人的時(shí)候,群里的回答也是讓我大寫的服...一個(gè)個(gè)的都不認(rèn)字嗎?
有人回答說(shuō)用 shiro 吧....有人回答說(shuō),誰(shuí)還用 JSP... 有人回答說(shuō),自定義標(biāo)簽吧...有人回答說(shuō),用 hasrole 標(biāo)簽吧... url 沒(méi)用....我真是服了,,我求求你們,你們是怎么當(dāng)上程序員的啊!!!!!當(dāng)別人問(wèn)你們問(wèn)題的時(shí)候,,你們的回答也是大寫的服!!!!!! 別人用 jsp 咋了,,跟當(dāng)前問(wèn)的問(wèn)題有任何關(guān)系嗎?所以啊有什么問(wèn)題還是靠自己解決啊.. 于是就跟蹤源代碼DefaultWebInvocationPrivilegeEvaluator. java中有個(gè)securityInterceptor屬性.這個(gè)屬性就決定是用擴(kuò)展自定義的類還是用 springsecurity 本身自己的類...最后發(fā)現(xiàn)是我這個(gè)地方?jīng)]有注入進(jìn)去..查詢了官方 API, 原來(lái)發(fā)現(xiàn) javaconfig 的方式在在
public void configure(WebSecurity web) throws Exception {
web.securityInterceptor(myFilterSecurityInterceptor);
web.privilegeEvaluator(customWebInvocationPrivilegeEvaluator());
}
這樣才能做到注入自己的擴(kuò)展的FilterSecurityInterceptor,下面我會(huì)給出詳細(xì)代碼.
參考文檔 http://docs.spring.io/spring-security/site/docs/4.2.2.BUILD-SNAPSHOT/reference/htmlsingle/
http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/config/annotation/web/builders/WebSecurity.html
解決問(wèn)題還是得靠自己. 多看文檔,多跟蹤源代碼,多看 API. 下面開(kāi)始進(jìn)入正題.
表設(shè)計(jì)
springsecurity框架的表設(shè)計(jì)還是很簡(jiǎn)單的, user 用戶表, role 角色表, resource 資源表.然后三者通過(guò)關(guān)系關(guān)聯(lián),我這里設(shè)計(jì)了5張表,
user,role,resource,user_role,role_resource其中user_role表是用戶與角色之間的關(guān)系,多對(duì)多,role_resource關(guān)系也是這樣.
實(shí)體類
user.java
@Entity
@Table(name = "ad_operator_info")
public class User extends BaseEntity {
/**
* 主鍵
*/
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
@Column(name = "oper_id", length = 32)
private String operId;
/**
* 用戶名
*/
@Column(name = "user_name")
private String userName;
/**
* 密碼
*/
private String password;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;
//省略 get... set..
}
role.java
@Entity
@Table(name = "ad_role")
public class Role extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
private Set<OperatorInfo> users;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "ad_roles_resources", joinColumns = {@JoinColumn(name = "rid")}, inverseJoinColumns = {@JoinColumn(name = "eid")})
private Set<Resource> resources;
// 省略 get set
}
Resource.java
@Entity
@Table(name = "ad_web_resource")
public class WebResource extends BaseEntity {
private static final long serialVersionUID = 7926081201477024763L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 主鍵
private String name; // 資源名稱
private String url;
@Column(name="remark",length=200)
private String remark;//備注
@Column(name="methodName",length=400)
private String methodName;//資源所對(duì)應(yīng)的方法名
@Column(name="methodPath",length=1000)
private String methodPath;//資源所對(duì)應(yīng)的包路徑
private String sn;
private String value; // 資源標(biāo)識(shí)
// 省略 get set 這里的屬性可以根據(jù)自己的業(yè)務(wù)來(lái).
}
實(shí)體類就此準(zhǔn)備完畢. 下面加入 springsecurity 的 jar 包
- 下載 jar
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
</dependencies>
2 .Spring Security配置
創(chuàng)建Spring Security的配置類 WebSecurityConfig,也是注入自己定義擴(kuò)展FilterSecurityInterceptor的重要類 ,具體如下:
import com.pwkj.potevio.adp.auth.MyFilterSecurityInterceptor;
import com.pwkj.potevio.adp.auth.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
/**
* Created by PrimaryKey on 17/2/4.
*
* @EnableWebSecurity: 禁用Boot的默認(rèn)Security配置,配合@Configuration啟用自定義配置(需要擴(kuò)展WebSecurityConfigurerAdapter)
* @EnableGlobalMethodSecurity(prePostEnabled = true): 啟用Security注解,例如最常用的@PreAuthorize
* configure(HttpSecurity): Request層面的配置,對(duì)應(yīng)XML Configuration中的<http>元素
* configure(WebSecurity): Web層面的配置,一般用來(lái)配置無(wú)需安全檢查的路徑
* configure(AuthenticationManagerBuilder): 身份驗(yàn)證配置,用于注入自定義身份驗(yàn)證Bean和密碼校驗(yàn)規(guī)則
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailService myUserDetailService;
@Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
@Bean
@Primary
public DefaultWebInvocationPrivilegeEvaluator customWebInvocationPrivilegeEvaluator() {
return new DefaultWebInvocationPrivilegeEvaluator(myFilterSecurityInterceptor);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
// javaconfig 配置是這樣 set 進(jìn)去的.
web.securityInterceptor(myFilterSecurityInterceptor);
web.privilegeEvaluator(customWebInvocationPrivilegeEvaluator());
web.
ignoring()
.antMatchers("/assets/**", "/login", "/login/success", "/kaptcha/**", "/**/*.jsp");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/resources", "/login", "/kaptcha/**").permitAll()//訪問(wèn):這些路徑 無(wú)需登錄認(rèn)證權(quán)限
.anyRequest().authenticated() //其他所有資源都需要認(rèn)證,登陸后訪問(wèn)
//.antMatchers("/resources").hasAuthority("ADMIN") //登陸后之后擁有“ADMIN”權(quán)限才可以訪問(wèn)/hello方法,否則系統(tǒng)會(huì)出現(xiàn)“403”權(quán)限不足的提示
.and()
.formLogin()
.loginPage("/")//指定登錄頁(yè)是”/”
.permitAll()
.successHandler(loginSuccessHandler()) //登錄成功后可使用loginSuccessHandler()存儲(chǔ)用戶信息,可選。
.and()
.logout()
.logoutUrl("/admin/logout")
.logoutSuccessUrl("/") //退出登錄后的默認(rèn)網(wǎng)址是”/home”
.permitAll()
.invalidateHttpSession(true);
// .and()
//.rememberMe()//登錄后記住用戶,下次自動(dòng)登錄,數(shù)據(jù)庫(kù)中必須存在名為persistent_logins的表
//.tokenValiditySeconds(1209600);
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//指定密碼加密所使用的加密器為passwordEncoder()
//需要將密碼加密后寫入數(shù)據(jù)庫(kù)
auth.userDetailsService(myUserDetailService);//.passwordEncoder(bCryptPasswordEncoder());
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
@Bean
public LoginSuccessHandler loginSuccessHandler() {
return new LoginSuccessHandler();
}
}
編寫LoginSuccessHandler.java 此類是在登陸成功之后做一些業(yè)務(wù)操作
package com.pwkj.potevio.adp.config;
import com.pwkj.potevio.adp.entity.OperatorInfo;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by PrimaryKey on 17/2/4.
*/
public class LoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
//獲得授權(quán)后可得到用戶信息 可使用OperatorInfoService進(jìn)行數(shù)據(jù)庫(kù)操作
OperatorInfo userDetails = (OperatorInfo) authentication.getPrincipal();
/* Set<SysRole> roles = userDetails.getSysRoles();*/
//輸出登錄提示信息
System.out.println("管理員 " + userDetails.getName() + " 登錄");
System.out.println("IP :" + getIpAddress(request));
super.onAuthenticationSuccess(request, response, authentication);
}
public String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
下面是自定義的過(guò)濾器,也是最重要的集成代碼.
首先編寫 MyInvocationSecurityMetadataSource.java 此類是首先加載的,用于加載資源配置.用resourceMap對(duì)象存儲(chǔ)url --> value
package com.pwkj.potevio.adp.auth;
/**
* Created by PrimaryKey on 17/2/4.
*/
import com.pwkj.potevio.adp.dao.WebResourceDao;
import com.pwkj.potevio.adp.entity.WebResource;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
@Service
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
private org.slf4j.Logger LOG = LoggerFactory.getLogger(getClass());
@Autowired
private WebResourceDao webResourceDao;
/**
* 加載資源,初始化資源變量
*/
@PostConstruct
public void loadResourceDefine() {
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<WebResource> resources = webResourceDao.findAll();
for (WebResource resource : resources) {
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
ConfigAttribute configAttribute = new SecurityConfig(resource.getValue());
configAttributes.add(configAttribute);
resourceMap.put(resource.getUrl(), configAttributes);
}
}
LOG.info("security info load success!!");
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if (resourceMap == null) loadResourceDefine();
String requestUrl = ((FilterInvocation) object).getRequestUrl();
// 返回當(dāng)前 url 所需要的權(quán)限
return resourceMap.get(requestUrl);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
其次編寫 MyUserDetailService.java 此類用來(lái)獲取用戶的所有權(quán)限.
package com.pwkj.potevio.adp.auth;
import com.pwkj.potevio.adp.entity.OperatorInfo;
import com.pwkj.potevio.adp.entity.Role;
import com.pwkj.potevio.adp.entity.WebResource;
import com.pwkj.potevio.adp.service.OperatorInfoService;
import com.pwkj.potevio.adp.service.WebResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* Created by PrimaryKey on 17/2/4.
* 二
*/
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
private OperatorInfoService operatorInfoService;
@Autowired
private WebResourceService webResourceService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//取得用戶
OperatorInfo operatorInfo = operatorInfoService.findByUserName(userName);
if (operatorInfo == null) {
throw new UsernameNotFoundException("UserName " + userName + " not found");
}
// 取得用戶的權(quán)限
Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(operatorInfo);
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
for (Role role : operatorInfo.getRoles()) {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
// 封裝成spring security的user
User userDetail = new User(operatorInfo.getUserName(), operatorInfo.getPassword(),
true,//是否可用
true,//是否過(guò)期
true,//證書(shū)不過(guò)期為true
true,//賬戶未鎖定為true ,
grantedAuths);
return userDetail;
}
// 取得用戶的權(quán)限
private Set<GrantedAuthority> obtionGrantedAuthorities(OperatorInfo operatorInfo) {
List<WebResource> resources = new ArrayList<WebResource>();
//獲取用戶的角色
Set<Role> roles = operatorInfo.getRoles();
for (Role role : roles) {
Set<WebResource> res = role.getResources();
for (WebResource resource : res) {
resources.add(resource);
}
}
Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
for (WebResource r : resources) {
//用戶可以訪問(wèn)的資源名稱(或者說(shuō)用戶所擁有的權(quán)限)
authSet.add(new SimpleGrantedAuthority(r.getValue()));
}
return authSet;
}
}
```
再次編寫 ```MyFilterSecurityInterceptor.java``` 用于跳轉(zhuǎn)
```
package com.pwkj.potevio.adp.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import javax.servlet.*;
import java.io.IOException;
/**
* Created by PrimaryKey on 17/2/4.
*
* 三
*/
@Service
public class MyFilterSecurityInterceptor extends FilterSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//fi里面有一個(gè)被攔截的url
//里面調(diào)用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個(gè)方法獲取fi對(duì)應(yīng)的所有權(quán)限
//再調(diào)用MyAccessDecisionManager的decide方法來(lái)校驗(yàn)用戶的權(quán)限是否足夠
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//執(zhí)行下一個(gè)攔截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
```
最后編寫```MyAccessDecisionManager.java``` 類用來(lái)判斷當(dāng)前用戶是否有訪問(wèn)權(quán)限.
```
package com.pwkj.potevio.adp.auth;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Iterator;
/**
* Created by PrimaryKey on 17/2/4.
*
* 最后一個(gè)類
*/
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
// TODO 權(quán)限 .... >>>
if (configAttributes == null) {
return;
}
//所請(qǐng)求的資源擁有的權(quán)限(一個(gè)資源對(duì)多個(gè)權(quán)限)
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
//訪問(wèn)所請(qǐng)求資源所需要的權(quán)限
String needPermission = configAttribute.getAttribute();
//用戶所擁有的權(quán)限authentication
for (GrantedAuthority ga : authentication.getAuthorities()) {
System.out.println("-----------PrimaryKey-----------ga.getAuthority()值=" + ga.getAuthority() + "," + "當(dāng)前類=MyAccessDecisionManager.decide()");
if (needPermission.equals(ga.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("沒(méi)有權(quán)限訪問(wèn)!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
```
到此java 代碼就已經(jīng)完成編寫了. 然后抓緊時(shí)間寫個(gè)```LoginController``` 吧
```
@PostMapping("/login")
public String login(String userName, String password,Model model) {
HttpSession session = request.getSession();
User user = userService.findByUserName(userName);
if (!passwordEncoder.matches(password, user.getPassword())) {
model.addAttribute("error", "用戶名或密碼錯(cuò)誤");
return "/pages/login";
}
// 這句代碼會(huì)自動(dòng)執(zhí)行咱們自定義的 ```MyUserDetailService.java``` 類
Authentication authentication = myAuthenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, password));
if (!authentication.isAuthenticated()) {
throw new BadCredentialsException("Unknown username or password");
}
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
session.setAttribute(PlatformConstant.SESSION_OPERATOR, user);
operateLogService.saveOperateLog(user, request.getRemoteAddr());
return "index";
}
```
頁(yè)面如下
```login.jsp```
```
<form action="/user/login"method="POST">
<table>
<tr>
<td>username:</td>
<td><input type='text'name='username'></td>
</tr>
<tr>
<td>password:</td>
<td><input type='password'name='password'></td>
</tr>
<tr>
<td><input name="reset"type="reset"></td>
<td><input name="submit"type="submit"></td>
</tr>
</table>
</form>
```
登陸之后跳轉(zhuǎn)到 index.jsp
```
這是首頁(yè),歡迎<sec:authentication property="name"/>!<br>
<sec:authentication property="authorities"/> <br/>
<a href="admin.jsp">進(jìn)入admin頁(yè)面</a>
<sec:authorize url='/other1.jsp' >
<a href="other1.jsp">權(quán)限1</a>
</sec:authorize>
<sec:authorize url='/other2.jsp' >
<a href="other2.jsp">權(quán)限2</a>
</sec:authorize>
<sec:authorize url='/other3.jsp' >
<a href="other3.jsp">權(quán)限3</a>
</sec:authorize>
```
到此,整個(gè)標(biāo)簽庫(kù)都會(huì)生效了,,,由于時(shí)間有限,,寫的有點(diǎn)倉(cāng)促了,哪里不懂的可以問(wèn)我.小伙伴抓緊時(shí)間試試吧...