最近,在实习中老师布置一个任务使用spring boot实现 用户管理和用户权限管理。在这里记录一下,以便日后学习。
网站流程图如下:
代码结构如下:
主要结构功能如下:
1.config:实现Spring Security的配置,定义用户角色权限、登录拦截以及相关url对应权限拦截
2.controller:定义控制层、路由处理
3.dao:定义数据层,访问
4.model:定义数据库存储实体类,使用Hibernate实现ORM映射
5.service:定义实现了UserDetails接口的实体类访问服务
Spring Boot 相比之前用过得SSH、SpringMVC + Spring + Hibernate,更加的轻量级,配置很少,包括前端控制器等均不需要。只有一个application.properties配置文件。言归正传、上代码!
首先,定义我们用到的两个实体类:分别为User 和Role,用于实现用户类和角色类:
User.java:
package com.adc.gim.models;
import org.hibernate.validator.constraints.NotEmpty;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by YanMing on 2017/5/17.
*/
@Entity
@Table(name="myuser")
public class User implements UserDetails{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "email")
@NotNull
//@NotEmpty(message="邮箱不能为空")
private String email;
//@NotEmpty(message="用户名不能为空")
@Column(name = "username")
@NotNull
private String username;
//@NotEmpty(message="密码不能为空")
@Column(name = "password")
@NotNull
private String password;
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
private List<Role> roles;
public User(){
}
public User(String email, String name,String password) {
this.email = email;
this.username = name;
this.password = password;
}
// Getter and setter methods
public Long getId() {
return id;
}
public void setId(long value) {
this.id = value;
}
public String getEmail() {
return email;
}
public void setEmail(String value) {
this.email = value;
}
public String getUsername() {
return username;
}
public void setUsername(String value) {
this.username = value;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// -------------->
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<>();
List<Role> roles = this.getRoles();
for (Role role : roles) {
auths.add(new SimpleGrantedAuthority(role.getRolename()));
}
return auths;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
Role.java:
package com.adc.gim.models;
/**
* Created by YanMing on 2017/5/18.
*/
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Table(name="myrole")
public class Role {
@Id
@GeneratedValue
private Long id;
private String rolename;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRolename() {
return rolename;
}
public void setRolename(String rolename) {
this.rolename = rolename;
}
}
在这里,有几个要讲解的地方:
1.用户和角色是多对多的关系,所以在User里面的roles,我们需要写上@ManyToMany 定义cascade类型
2.实现UserDetails接口:
在Spring Security中,定义了UserDetailsService,这个接口使一个DAO接口,主要用于加载用户数据。所以我们的用户类需要继承UserDetails。这样的话,就会通过Spring Security,在我们登录时访问到数据库,然后比对密码、权限等。
接下来就是DAO层继承了JpaRepository的UserDao用于访问数据库
UserDao.java:
package com.adc.gim.dao;
import com.adc.gim.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.transaction.Transactional;
/**
* Created by YanMing on 2017/5/17.
*/
@Transactional
public interface UserDao extends JpaRepository<User,Long>{
public User findByEmail(String email);
public User findByUsername(String name);
}
Service层继承UserDetailsService
package com.adc.gim.service;
import com.adc.gim.dao.UserDao;
import com.adc.gim.models.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* Created by YanMing on 2017/5/18.
*/
public class CustomUserService implements UserDetailsService {
@Autowired
UserDao userRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findByUsername(s
);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
System.out.println("s:"+s);
System.out.println("username:"+user.getUsername()+";password:"+user.getPassword());
return user;
}
}
接下来,就是要配置我们的Spring Security:
package com.adc.gim.config;
import com.adc.gim.service.CustomUserService;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
/**
* Created by Deng Fuping on 2017/5/18.
*/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService userService(){
return new CustomUserService();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/login", "/register","/create", "/css/**", "/js/**", "/images/**", "/fonts/**").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/cmnuser").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedPage("/accessDenied")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/loginIn", true)
.permitAll()
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login")
.permitAll();
}
/*@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}*/
}
定义了用户角色的权限对应的url访问。设置登录url,登出url等信息,以及匹配特定的url对应用户角色。这部分都很简单。
需要解释的地方是:
.loginPage("/login")
这个的意思是,会将所有需要登录的url访问全部都定向到..../login(GET),然后再Controller中使用RequestMapping,将该url定向到登录界面。设置登录按钮对应提交url为 ..../login(POST),这样Spring Security会通过前面提到的UserDetails加载你的用户信息,然后进行密码校验。如果密码正确,定向到登录成功界面。
同时,对应的.../logout会自动调用内部的登出函数,清除Session,实现登出。
Controller.java这部分比较简单:
package com.adc.gim.controllers;
import com.adc.gim.models.Role;
import com.adc.gim.models.User;
import com.adc.gim.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by YanMing on 2017/5/17.
*/
@Controller
@RequestMapping("/")
public class MainController {
@Autowired
private UserDao userDao;
private List<Role> createUserRole(){
List<Role> tmpList = new ArrayList<Role>();
Role newRole = new Role();
Long newId = 2L;
newRole.setId(newId);
newRole.setRolename("ROLE_USER");
tmpList.add(newRole);
return tmpList;
}
private List<Role> createAdminRole(){
List<Role> tmpList = new ArrayList<Role>();
Role newRole = new Role();
Long newId = 1L;
newRole.setId(newId);
newRole.setRolename("ROLE_ADMIN");
tmpList.add(newRole);
return tmpList;
}
@RequestMapping(value = {"/home","/"})
public String homePage() {
return "home";
}
@RequestMapping(value = "/register")
public ModelAndView registerPage() {
ModelAndView model = new ModelAndView();
User etpUser = new User();
model.addObject(etpUser);
//model.setViewName("registerPage");
model.setViewName("signup");
return model;
}
@RequestMapping(value = "/login",method = RequestMethod.GET)
public ModelAndView loginPage() {
ModelAndView model = new ModelAndView();
User etpUser = new User();
model.addObject(etpUser);
//model.setViewName("loginPage");
model.setViewName("signin");
return model;
}
@RequestMapping(value = "/create",method = RequestMethod.GET)
public ModelAndView registerUser(@ModelAttribute(value = "user") User user, BindingResult result) {
ModelAndView model = new ModelAndView();
if (result.hasErrors()) {
List<ObjectError> list = result.getAllErrors();
for(ObjectError error:list){
System.out.println(error.getCode()+"---"+error.getArguments().toString()+"---"+error.getDefaultMessage());
}
model.setViewName("signup");
model.addObject(user);
} else {
try {
System.out.print(user.getUsername() + user.getEmail()+user.getPassword());
List<Role> tmpList = createUserRole();
user.setRoles(tmpList);
userDao.save(user);
//model.setViewName("registerOK");
model.setViewName("signin");
model.addObject(user);
} catch (Exception ex) {
System.out.print(ex.toString());
model.setViewName("failPage");
}
}
return model;
}
@RequestMapping(value = "/loginIn",method = RequestMethod.GET)
public ModelAndView loginPost(){
ModelAndView model = new ModelAndView();
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
String curName = userDetails.getUsername();
//System.out.println(curName);
User user = userDao.findByUsername(curName);
if(user == null){
model.setViewName("failPage");
}else {
model.addObject(user);
//Role role = new Role();
//model.addObject(role);
//model.setViewName("loginOk");
model.setViewName("workPage");
}
return model;
}
@RequestMapping(value = "/fail")
public ModelAndView failPage() {
ModelAndView model = new ModelAndView();
model.setViewName("failPage");
return model;
}
@RequestMapping(value = "/accessDenied")
public ModelAndView accessDndPage() {
ModelAndView model = new ModelAndView();
model.setViewName("accessDenied");
return model;
}
@RequestMapping(value = "regByAdmin",method = RequestMethod.GET)
@ResponseBody
public String regByAdmin(@RequestParam(value = "regName",required = false) String regName,
@RequestParam(value = "regPwd",required = false) String regPwd,
@RequestParam(value = "regEml",required = false) String regEml,
@RequestParam(value = "regRole",required = false) String regRole) {
ModelAndView model = new ModelAndView();
User newUser = new User(regEml,regName,regPwd);
String res = "wrong";
if(regRole.equals("role_user")){
List<Role> tmpList = createUserRole();
newUser.setRoles(tmpList);
}else {
List<Role> tmpList = createAdminRole();
newUser.setRoles(tmpList);
}
try {
userDao.save(newUser);
res = "right";
}catch (Exception ex){
}
return res;
}
}
在这里只贴出一张html的文件供参考,其余的可在项目中查看。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<link rel="shortcut icon" href="images/favicon.ico" />
<link rel="bookmark" href="images/favicon.ico" />
<title>注册</title>
<link href="css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1 style="text-align: center">用户注册</h1>
<br/><br/>
<div class="container">
<form th:action="@{/create}" th:object="${user}" method="get" th:method="get">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-user"></span> </div>
<input type="text" id="email" name="email" class="form-control" th:text="${user.email}" placeholder="邮箱"/>
</div>
<br/>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span> </div>
<input type="text" id="username" name="username" class="form-control" th:text="${user.username}" placeholder="用户名"/>
</div>
<br/>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span> </div>
<input type="password" id="password" name="password" class="form-control" th:text="${user.password}" placeholder="密 码"/>
</div>
<br/>
<input type="submit" class="form-control btn btn-success" value="注 册"/>
</form>
<br/>
<p style="text-align: left" id="hint">已注册?<a href="/login">快去登录</a><br/><br/></p>
</div>
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script>
/*$('username').blur(function () {
var username = $('#username').val();
$.get("/findByName",username,function () {
})
})*/
</script>
</body>
</html>
Thymeleaf部分可到Thymeleaf官网查看
最后,通过Spring boot的启动器启动,网站假设到此结束。所有的网站架设完毕。运行如下:
主页:
登录界面:
管理员用户显示界面:
普通用户显示界面:
访问拒绝界面:
P.S.文章不妥之处还望指正